Chapter 4: Write our Client Code
Now that we’ve written our server code, let’s move back to the client side. In Chapter 1 we created the UI on the design slide of the form, to write our code we’ll going to the aptly name Code tab.
Add the following code to your form:
from trexjacket import api
dashboard = api.get_dashboard()
First thing we will do is import the trexjacket
library we setup all the way back in the dependency section. Then we are going to setup the dashboard object.
Create a dictionary to house the Opportunity attributes we want to change. We’ll also call a function we haven’t created yet self.reset()
to clear the dictionary on screen load and refresh.
self.reset()
self.new_dict = {
'StageName': None,
'CloseDate': None,
'Amount': None
}
Call our get_opportunity_stages server function to fetch the list from Tableau. When calling server functions, we use the syntax anvil.server.call(function name', args, *kwargs)
Then we want to stick those stages into a drop-down element in our screen. When you want to reference a screen element, we use self
to reference the form class then the object name, self.object_name
.
We can assign a list to a drop-down property with items, self.drop_down_name.items
oppstages = anvil.server.call('opportunity_stages')
self.drop_down_stage.items = oppstages
To finish off the init we’re going to register the event handler. The event handler captures any selections the user makes in their dashboard so the extension can take action.
In our case, the event handler we give us the attributes of the selected Opportunity.
We want selection_changed and we’ll call our event handler self.my_form_event_handler
.
All the code in this section goes before the line self.init_components(**properties)
so it takes place before the form loads the UI.
dashboard.register_event_handler('selection_changed', self.my_form_event_handler)
self.init_components(**properties)
Functions
In this section we will create the functions for our class.
Event Handler
In the extension library the event is the thing that changes when a ‘selection_changed’ occurs. We want all the records from each change.
If something did change, we want to assign class variables with selected value, in our case the opportunity the user picked. We get the rest of the information using our server function get_opportunity.
The event handler closes with self.refresh_data_bindings()
. Refresh data binding will update any screen objects with the new values, you will want to do this after changing any bound variables. We will bind screen objects in a later section.
def my_form_event_handler(self, event):
selected_value = event.get_selected_marks()
if selected_value:
try:
self.opp_name = selected_value[0]['Name']
self.opp_ID = selected_value[0]['Opportunity ID']
self.opportunity = anvil.server.call('get_opportunity',selected_value[0]['Opportunity ID'])
self.opp_stage = self.opportunity['StageName']
self.opp_date = self.opportunity['CloseDate']
self.opp_amount = self.opportunity['Amount']
except KeyError:
pass
else:
self.reset()
self.refresh_data_bindings()
Reset
We’re adding reset to allow the user to clear the selected opportunity on page refresh or by clicking on a blank space in the dashboard.
def reset(self):
self.opp_name = None
self.opp_ID = None
self.opp_stage = None
self.opp_date = None
self.opp_amount = None
Clear Changes
Clear changes will change all the selections in our new_dict
to None, we call clear changes after we submit the API request to update the opportunity. This prevents the extension from sending unnecessary update requests.
def clear_changes(self):
self.new_dict = {x: None for x in self.new_dict}
Pick from the Drop-Down
On the Design view select your Opportunity Stage drop-down, scroll down on the right-side panel to the Container Properties section, click the blue button next to change.
This will open the split view and create the function drop_down_stage_change
.
Assign the selected value from the drop down to the StageName variable in our new_dict and refresh the data bindings.
Anvil Documentation on Basic Components
def drop_down_stage_change(self, **event_args):
"""This method is called when an item is selected"""
self.new_dict['StageName'] = self.drop_down_stage.selected_value
self.refresh_data_bindings()
Change the Close Date
On the Design view select your Close Date date picker, scroll down on the right-side panel to the Container Properties section, click the blue button next to change.
This will open the split view and create the function date_picker_1_change
.
Assign the date from the date picker to the CloseDate variable in our new_dict
, convert it to iso format, then refresh the data bindings.
Anvil Documentation on Basic Components
def date_picker_1_change(self, **event_args):
"""This method is called when the selected date changes"""
self.new_dict['CloseDate'] = self.date_picker_1.date.isoformat()
self.refresh_data_bindings()
Enter an Amount
On the Design view select your Amount text box, scroll down on the right-side panel to the Container Properties section, click the blue button next to change.
This will open the split view and create the function text_box_amount_change
. Go back to the Design view then Container Properties then copy past text_box_amount_change
into the pressed_enter field as well.
Assign the selected value from the text box to the Amount variable in our new_dict, convert the text to an Integer.
Assign the same integer to the opp_amount
variable.
Refresh the data bindings.
Anvil Documentation on Basic Components
def text_box_amount_change(self, **event_args):
"""This method is called when the text in this text box is edited"""
self.new_dict['Amount'] = int(self.text_box_amount.text)
self.opp_amount = int(self.text_box_amount.text)
self.refresh_data_bindings()
Your form code should now look like this:
1from ._anvil_designer import TB_FormTemplate
2from anvil import *
3from anvil import tableau
4import anvil.tables as tables
5import anvil.tables.query as q
6from anvil.tables import app_tables
7import anvil.server
8
9from trexjacket import api
10dashboard = api.get_dashboard()
11
12class TB_Form(TB_FormTemplate):
13 def __init__(self, **properties):
14 self.reset()
15 self.new_dict = {
16 'StageName': None,
17 'CloseDate': None,
18 'Amount': None
19 }
20 oppstages = anvil.server.call('opportunity_stages')
21 self.drop_down_stage.items = oppstages
22 dashboard.register_event_handler('selection_changed', self.my_form_event_handler)
23 self.init_components(**properties)
24
25 def reset(self):
26 self.opp_name = None
27 self.opp_ID = None
28 self.opp_stage = None
29 self.opp_date = None
30 self.opp_amount = None
31
32 def my_form_event_handler(self, event):
33 selected_value = event.get_selected_marks()
34 if selected_value:
35 try:
36 self.opp_name = selected_value[0]['Name']
37 self.opp_ID = selected_value[0]['Opportunity ID']
38 self.opportunity = anvil.server.call('get_opportunity',selected_value[0]['Opportunity ID'])
39 self.opp_stage = self.opportunity['StageName']
40 self.opp_date = self.opportunity['CloseDate']
41 self.opp_amount = self.opportunity['Amount']
42 except KeyError:
43 pass
44 else:
45 self.reset()
46 self.refresh_data_bindings()
47
48 def clear_changes(self):
49 self.new_dict = {x: None for x in self.new_dict}
50
51 def button_submit_click(self, **event_args):
52 response = anvil.server.call('update_opportunity',self.opp_ID,self.new_dict)
53 if response:
54 self.clear_changes()
55 self.refresh_data_bindings()
56 else:
57 alert('Please make a change before submitting.')
58
59 def drop_down_stage_change(self, **event_args):
60 self.new_dict['StageName'] = self.drop_down_stage.selected_value
61 self.refresh_data_bindings()
62
63 def date_picker_1_change(self, **event_args):
64 self.new_dict['CloseDate'] = self.date_picker_1.date.isoformat()
65 self.refresh_data_bindings()
66
67 def text_box_amount_change(self, **event_args):
68 self.new_dict['Amount'] = int(self.text_box_amount.text)
69 self.opp_amount = int(self.text_box_amount.text)