Add Component Code

Now that we have create the layout for the AddComponent, we need to write the code to run it. Eventually we want to save the details to a table, but we haven’t made a table yet, so we will simply display the data as a save message

Planning

There are two events that will trigger code:

  • loading the component → initialise variable to store the user input

  • clicking the Add Assessment button → validate the user’s input, display save message and reset the form.

Coding

Now we know what we are doing, open the AddComponent in Code mode.

Initialising variable

We need to create variables to store all the user’s input. This needs to occur in the __init__ method.

10class AddComponent(AddComponentTemplate):
11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14    self.subject = ""
15    self.details = ""
16    self.start = None
17    self.due = None

Code explaination

  • lines 14 & 15 → since Subject and Details store text, we create empty strings for their variables.

  • lines 16 & 15Start and Due are both dates, which are objects, so their empty variables are set using None

We also need to make sure the error message is invisible.

10class AddComponent(AddComponentTemplate):
11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14    self.subject = ""
15    self.details = ""
16    self.start = None
17    self.due = None
18
19    # Any code you write here will run before the form opens.
20    self.label_error.visible = False

Code explaination

  • line 20 → sets the label error to not vissible

Validation

Now we need to make the button_add_click event handler.

event handler

The first code we add to the button_add_click event handler checks that the user has entered data.

22  def button_add_click(self, **event_args):
23    # validation
24    if not self.text_box_subject.text:
25      self.label_error.visible = True
26      self.label_error.text = "Subject name needed"
27    elif not self.text_box_details.text:
28      self.label_error.visible = True
29      self.label_error.text = "Assessment details needed"
30    elif not self.date_picker_start.date:
31      self.label_error.visible = True
32      self.label_error.text = "Start date needed"
33    elif not self.date_picker_due.date:
34      self.label_error.visible = True
35      self.label_error.text = "Due date needed"

Code explaination

  • lines 24 & 27 → checks that the text box has a value (remember string truthiness)

  • lines 30 & 33 → checks that a date has been picked (remember object truthiness)

  • lines 25, 28, 31 & 34 → make the error message visible

  • lines 26, 29, 32 & 35 → display the error message

Validation testing

Launch your website and check that it produces an error message for each input element.

Save Message

Now we want to display a message to the user about what values will be saved.

22  def button_add_click(self, **event_args):
23    # validation
24    if not self.text_box_subject.text:
25      self.label_error.visible = True
26      self.label_error.text = "Subject name needed"
27    elif not self.text_box_details.text:
28      self.label_error.visible = True
29      self.label_error.text = "Assessment details needed"
30    elif not self.date_picker_start.date:
31      self.label_error.visible = True
32      self.label_error.text = "Start date needed"
33    elif not self.date_picker_due.date:
34      self.label_error.visible = True
35      self.label_error.text = "Due date needed"
36    else:
37      self.label_error.visible = True
38      self.subject = self.text_box_subject.text
39      self.details = self.text_box_details.text
40      self.start = self.date_picker_start.date
41      self.due = self.date_picker_due.date
42      self.label_error.text = f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded"

Code explaination

  • line 37 → makes the message available

  • lines 38 - 41 → reads the values from the form and stores them in the variables

  • line 42 → creates a string displaying the message and displays it in the error message label.

Save testing

Launch your website and check if it works. Enter a subject, assessment details, start date and due date, then click the Add Assessment button.

testing save

We can make a few observations from this test:

  1. The save message should look the same as the error message

  2. When the same message is presented, the input values in the form should be reset

Let’s fix those up.

Fixing messaging

The problem we have with the save message is that it is using the same format at the error message. There are two solutions:

  1. Add another label for the save message, or

  2. Change the format of the error label.

Neither one has an advantage over the other, so, for no particular reason, we will change the format of the error label.

This will involve changing the following label properties:

  • foreground colour

  • icon

  • bold

If we look at our code, this will need to happen for each of the error messages, as well as the save message. That means in five different locations. This is started to get quite repetitive. If we put this code through, and then decide to change the error foreground colour, we will need to fix it in multiple places. This is a classic example of the DRY principle. The means, it’s time to refactor.

Refactor plan

What we will do is create two methods to:

  • display an error message

  • display a save message

These methods will contain the appropriate formatting and then change the label text.

Speaking of the label, the name label_error doesn’t seem appropriate now, sow we will change it to label_message.

So, before refactoring the code, first change back to the Design mode and change the name of label_error to label_message.

change label

Display error

Now, back in the Code mode, we will first create the display_error method

Under the button_add_click handler add the following code (be careful with your indentations).

44  def display_error(self, message):
45    self.label_message.visible = True
46    self.label_message.foreground = "#ff0000"
47    self.label_message.icon = "fa:exclamation-triangle"
48    self.label_message.bold = True
49    self.label_message.text = message

Code explaination

  • line 44 → creates the display_error method, which accepts an argument called message

  • line 45 → turns the label visible

  • line 46 → changes the font colour to red

  • line 47 → sets the label’s icon

  • line 48 → makes the label bold

  • line 49 → makes the label display the message passed to it as an argument

Now go back to the button_add_click handler and replace all the error label code with a call to display_error.

22  def button_add_click(self, **event_args):
23    # validation
24    if not self.text_box_subject.text:
25      self.display_error("Subject name needed")
26    elif not self.text_box_details.text:
27      self.display_error("Assessment details needed")
28    elif not self.date_picker_start.date:
29      self.display_error("Start date needed")
30    elif not self.date_picker_due.date:
31      self.display_error("Due date needed")
32    else:
33      self.label_error.visible = True
34      self.subject = self.text_box_subject.text
35      self.details = self.text_box_details.text
36      self.start = self.date_picker_start.date
37      self.due = self.date_picker_due.date
38      self.label_error.text = f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded"

Display save

Now to add the display_save method.

Add the following code under the display_error method (remember to check indentation).

47  def display_save(self, message):
48    self.label_message.visible = True
49    self.label_message.foreground = "#000000"
50    self.label_message.icon = "fa:save"
51    self.label_message.bold = False
52    self.label_message.text = message

Code explaination

  • line 47 → creates the display_save method, which accepts an argument called message

  • line 48 → turns the label visible

  • line 49 → changes the font colour to black

  • line 50 → sets the label’s icon

  • line 51 → makes the label not bold

  • line 52 → makes the label display the message passed to it as an argument

Now go back to the button_add_click handler and replace the save message code with a call to display_save.

32    else:
33      self.subject = self.text_box_subject.text
34      self.details = self.text_box_details.text
35      self.start = self.date_picker_start.date
36      self.due = self.date_picker_due.date
37      self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")

Remove any other reference to label_error

Use Anvil’s search function to check if there are any more references to label_error in AddComponent and then fix them.

search

Test messaging

Launch your website and check that all the error messages and the save message work as planned.

test message

Clear Form

The final step is to clear the user input and ready the form for the next assessment.

Under the display_save method add the following code:

54  def reset_form(self):
55    self.subject = ""
56    self.details = ""
57    self.start = None
58    self.due = None
59    self.text_box_subject.text = ""
60    self.text_box_details.text = ""
61    self.date_picker_start.date = None
62    self.date_picker_due.date = None

Code explaination

  • line 54 → creates the reset_form method

  • lines 55 - 58 → returns all the variables to their initialisation state

  • lines 59 - 62 → clears all the input from the elements

Since we only want to clear the form after it’s been saved, call the function from within the else section of the button_add_click handler.

32    else:
33      self.subject = self.text_box_subject.text
34      self.details = self.text_box_details.text
35      self.start = self.date_picker_start.date
36      self.due = self.date_picker_due.date
37      self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
38      self.reset_form()

Test clear form

Launch your website one more time and correctly enter all the required data. When you click the Add Assessment button, the save message should appear and all the input should be cleared.

test clear

Final code state

By the end of this tutorial your code should be the same as below:

Final AddComponent

 1from ._anvil_designer import AddComponentTemplate
 2from anvil import *
 3import anvil.server
 4import anvil.tables as tables
 5import anvil.tables.query as q
 6from anvil.tables import app_tables
 7import anvil.users
 8
 9
10class AddComponent(AddComponentTemplate):
11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14    self.subject = ""
15    self.details = ""
16    self.start = None
17    self.due = None
18
19    # Any code you write here will run before the form opens.
20    self.label_message.visible = False
21
22  def button_add_click(self, **event_args):
23    # validation
24    if not self.text_box_subject.text:
25      self.display_error("Subject name needed")
26    elif not self.text_box_details.text:
27      self.display_error("Assessment details needed")
28    elif not self.date_picker_start.date:
29      self.display_error("Start date needed")
30    elif not self.date_picker_due.date:
31      self.display_error("Due date needed")
32    else:
33      self.subject = self.text_box_subject.text
34      self.details = self.text_box_details.text
35      self.start = self.date_picker_start.date
36      self.due = self.date_picker_due.date
37      self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
38      self.reset_form()
39
40  def display_error(self, message):
41    self.label_message.visible = True
42    self.label_message.foreground = "#ff0000"
43    self.label_message.icon = "fa:exclamation-triangle"
44    self.label_message.bold = True
45    self.label_message.text = message
46
47  def display_save(self, message):
48    self.label_message.visible = True
49    self.label_message.foreground = "#000000"
50    self.label_message.icon = "fa:save"
51    self.label_message.bold = False
52    self.label_message.text = message
53
54  def reset_form(self):
55    self.subject = ""
56    self.details = ""
57    self.start = None
58    self.due = None
59    self.text_box_subject.text = ""
60    self.text_box_details.text = ""
61    self.date_picker_start.date = None
62    self.date_picker_due.date = None