- Implementing event handlers for edit and save buttons.
- Transferring assessment details to edit fields and toggling element visibility.
- Writing updates to a backend Assessments table.
- Updating display elements after saving.
- Testing the functionality to ensure correct behavior.
The code is going to react to two events:
- button_edit click
- button_save click
So, lets go ahead and create those two event handlers.
Open the AssessmentPanel in Design mode:
- click on each button
- select on click event
When the user clicks the edit button, we want to web app to:
- take the values of the assessment item from this specific panel and write them into the edit elements
- make all the display element invisible
- make all the edit elements visible
Open the AssessmentPanel in Code mode.
In the button_edit_click handler use the code below to copy the values from the display elements.
:linenos:
:lineno-start: 27
:emphasize-lines: 2-5
def button_edit_click(self, **event_args):
self.text_box_subject.text = self.item["subject"]
self.text_box_details.text = self.item["details"]
self.date_picker_start.date = self.item["start_date"]
self.date_picker_due.date = self.item["due_date"]
:class: notice
- **line 28** → takes **subject** value for specific assessment for this panel and saves it as the text for the subject text box.
- **line 29** → takes **details** value for specific assessment for this panel and saves it as the text for the details text box.
- **line 30** → takes **start_date** value for specific assessment for this panel and saves it as the date for the start date picker
- **line 31** → takes **due_date** value for specific assessment for this panel and saves it as the date for the due date picker
Now we need to change the visibility of the elements. To do this we will create a separate function that swaps the visibility of each element, ie. if visibility was True
it will make it False
.
We will do this with one function that will work for both event handlers. Add the highlighted code below to the bottom of the AssessmentPanel code.
:linenos:
:lineno-start: 33
:emphasize-lines: 5-18
def button_save_click(self, **event_args):
"""This method is called when the button is clicked"""
pass
def switch_components(self):
# display elements
self.label_subject.visible = not self.label_subject.visible
self.label_details.visible = not self.label_details.visible
self.label_start.visible = not self.label_start.visible
self.label_due.visible = not self.label_due.visible
self.button_edit.visible = not self.button_edit.visible
# edit elements
self.text_box_subject.visible = not self.text_box_subject.visible
self.text_box_details.visible = not self.text_box_details.visible
self.date_picker_start.visible = not self.date_picker_start.visible
self.date_picker_due.visible = not self.date_picker_due.visible
self.button_save.visible = not self.button_save.visible
:class: notice
- **line 37** → create the **switch_components** method
- **lines 39 - 43** → swaps the **visibility** value for each of the display elements
- **lines 46 - 50** → swaps the **visibility** value for each of the edit elements
Finally, to call the switch_components method from the button_edit_click handler, add the highlighted code.
:linenos:
:lineno-start: 27
:emphasize-lines: 6
def button_edit_click(self, **event_args):
self.text_box_subject.text = self.item["subject"]
self.text_box_details.text = self.item["details"]
self.date_picker_start.date = self.item["start_date"]
self.date_picker_due.date = self.item["due_date"]
self.switch_components()
:class: notice
- **line 32** → called the **switch_component** method
Launch your website and test if the edit button works. You may need to adjust the width of your columns to make everything fit.
If you have fixed you column widths, then it is time to make the save button work.
When the user clicks the Save button we want the following to happen:
- take the values of all the edit elements and write them the Assessments table
- take the values from the edit elements and write them to the display elements
- make all the edit elements invisible
- make all the display elements visible
To achieve the first step, we will need to call a backend function to save the values to the Assessments table. So open the assessments_service, and add the highlighted code to the end.
:linenos:
:lineno-start: 26
:emphasize-lines: 7 - 15
@anvil.server.callable
def update_assessment_completed(assessment_id, completed):
assessment = app_tables.assessments.get_by_id(assessment_id)
if assessment:
assessment["completed"] = completed
@anvil.server.callable
def update_assessment(assessment_id, subject, details, start_date, due_date, completed):
assessment = app_tables.assessments.get_by_id(assessment_id)
if assessment:
assessment['subject'] = subject
assessment['details'] = details
assessment['start_date'] = start_date
assessment['due_date'] = due_date
assessment['completed'] = completed
:class: notice
- **line 26** → makes this function callable by the frontend
- **line 27** → create the `update_assessments` function which requires the following data to be passed:
- assessment_id
- subject
- details
- start_date
- due_date
- completed
- **line 34** → loads the assessment that has the passed `assessment_id`
- **line 35** → checks to see if there is an assessment with the passed `assessment_id`
- **lines 36 - 40** → overwrites the stored values of the assessment with the values passed
Now that we have our backend function to call, we can work on the button_save_click handler.
Open the AssessmentPanel in code mode.
First thing we will do is write the values to the Assessments table. Go to the button_save_click handler and replace lines 35 and 36 with the highlighted code below:
:linenos:
:lineno-start: 34
:emphasize-lines: 2 - 9
def button_save_click(self, **event_args):
# write to server
anvil.server.call('update_assessment',
self.item.get_id(),
self.text_box_subject.text,
self.text_box_details.text,
self.date_picker_start.date,
self.date_picker_due.date,
self.check_box_completed.checked)
:class: notice
- **line 35** → a comment to help structure the method
- **lines 36 - 42** → calls the function we just created in the **assessment_service**. The first argument needs to be the name of the function being called. The other arguments are the date the function needs passed.
- **line 37** → gets the id of the assessment for this specific panel.
Next we need to take all the values from the edit elements and write them to the display elements, so the display reflects the changes made.
Add the highlighted code to the end of the button_save_click event handler.
:linenos:
:lineno-start: 34
:emphasize-lines: 11 - 15
def button_save_click(self, **event_args):
# write to server
anvil.server.call('update_assessment',
self.item.get_id(),
self.text_box_subject.text,
self.text_box_details.text,
self.date_picker_start.date,
self.date_picker_due.date,
self.check_box_completed.checked)
# update display
self.label_subject.text = self.text_box_subject.text
self.label_details.text = self.text_box_details.text
self.label_start.text = self.date_picker_start.date.strftime('%d/%m/%Y')
self.label_due.text = self.date_picker_due.date.strftime('%d/%m/%Y')
:class: notice
- **line 44** → a comment to help structure the method
- **line 45** → assigns the value in the subject text box to the subject label
- **line 46** → assigns the value in the details text box to the details label
- **line 47** → assigns the date in the start date picker to the start label
- **line 48** → assigns the date in the due date picker to the due label
Finally, we need to turn all the edit elements invisible and all the display elements visible. Luckily we created a method that swaps all those elements visibility values, so we can just call that.
:linenos:
:lineno-start: 34
:emphasize-lines: 16
def button_save_click(self, **event_args):
# write to server
anvil.server.call('update_assessment',
self.item.get_id(),
self.text_box_subject.text,
self.text_box_details.text,
self.date_picker_start.date,
self.date_picker_due.date,
self.check_box_completed.checked)
# update display
self.label_subject.text = self.text_box_subject.text
self.label_details.text = self.text_box_details.text
self.label_start.text = self.date_picker_start.date.strftime('%d/%m/%Y')
self.label_due.text = self.date_picker_due.date.strftime('%d/%m/%Y')
self.switch_components()
- line 49 → call the switch component method to reverse the visibility of the edit and display elements.
You should be able to change the values of your assessments. Launch your webapp, and try to change the details. Make sure that you test all values (subject, details, start date and due date).
By the end of this tutorial your code should be the same as below:
:linenos:
from ._anvil_designer import AssessmentPanelTemplate
from anvil import *
import anvil.server
import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import datetime
class AssessmentPanel(AssessmentPanelTemplate):
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
# Any code you write here will run before the form opens.
self.check_box_completed.checked = self.item['completed']
self.label_subject.text = self.item['subject']
self.label_details.text = self.item['details']
self.label_start.text = self.item['start_date'].strftime('%d/%m/%Y')
self.label_due.text = self.item['due_date'].strftime('%d/%m/%Y')
def check_box_completed_change(self, **event_args):
new_value = self.check_box_completed.checked
anvil.server.call('update_assessment_completed', self.item.get_id(),new_value)
def button_edit_click(self, **event_args):
self.text_box_subject.text = self.item["subject"]
self.text_box_details.text = self.item["details"]
self.date_picker_start.date = self.item["start_date"]
self.date_picker_due.date = self.item["due_date"]
self.switch_components()
def button_save_click(self, **event_args):
# write to server
anvil.server.call('update_assessment',
self.item.get_id(),
self.text_box_subject.text,
self.text_box_details.text,
self.date_picker_start.date,
self.date_picker_due.date,
self.check_box_completed.checked)
# update display
self.label_subject.text = self.text_box_subject.text
self.label_details.text = self.text_box_details.text
self.label_start.text = self.date_picker_start.date.strftime('%d/%m/%Y')
self.label_due.text = self.date_picker_due.date.strftime('%d/%m/%Y')
self.switch_components()
def switch_components(self):
# display elements
self.label_subject.visible = not self.label_subject.visible
self.label_details.visible = not self.label_details.visible
self.label_start.visible = not self.label_start.visible
self.label_due.visible = not self.label_due.visible
self.button_edit.visible = not self.button_edit.visible
# edit elements
self.text_box_subject.visible = not self.text_box_subject.visible
self.text_box_details.visible = not self.text_box_details.visible
self.date_picker_start.visible = not self.date_picker_start.visible
self.date_picker_due.visible = not self.date_picker_due.visible
self.button_save.visible = not self.button_save.visible
:linenos:
import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.server
@anvil.server.callable
def add_assessment(subject, details, start_date, due_date):
user = anvil.users.get_user()
app_tables.assessments.add_row(user= user,
subject= subject,
details=details,
start_date=start_date,
due_date=due_date,
completed=False)
@anvil.server.callable
def get_assessment():
user = anvil.users.get_user()
return app_tables.assessments.search(tables.order_by('due_date'),
user=user,
completed=False)
@anvil.server.callable
def update_assessment_completed(assessment_id, completed):
assessment = app_tables.assessments.get_by_id(assessment_id)
if assessment:
assessment["completed"] = completed
@anvil.server.callable
def update_assessment(assessment_id, subject, details, start_date, due_date, completed):
assessment = app_tables.assessments.get_by_id(assessment_id)
if assessment:
assessment['subject'] = subject
assessment['details'] = details
assessment['start_date'] = start_date
assessment['due_date'] = due_date
assessment['completed'] = completed