Data Binding in Anvil Apps Demystified

Jay Shaffstall
Anvil
Published in
13 min readApr 3, 2024

Data Bindings are a useful feature in Anvil that make it easy to keep your UI components in sync with your data. A Data Binding associates a property of a component with a single Python expression, saving you from repeating similar sorts of assignments all over your code. In this tutorial we’re going to look at what Data Bindings are, how you can use them, and common gotchas.

We’re going to look at data bindings by developing an example app for logging our favorite movie. Our tutorial will have three main parts:

  1. We’ll build an app and use data bindings to display movie data
  2. Next, we’ll extend the functionality to edit the data
  3. Then, we’ll store the data to a Data Table
  4. Finally, we’ll make some changes so that edits to our data persist when the app is re-run

The final app will look something like this:

An image of the app showing information about the movie Back to the Future displayed.

All of this is done in Anvil’s hosted environment through your web browser, all in Python.

If you’re in a hurry, you can click here to clone the app and explore the finished version in Anvil.

If you’re completely new to Anvil, you may want to try a 5-minute tutorial before beginning this tutorial.

Let’s get started!

What is a Data Binding?

When you want to display the contents of a variable in a visual component you would normally use code like this:

self.label_1.text = self.title

If you wanted to update the Label component when self.title changed, you would need to repeat that line of code in each place the variable changed. A data binding allows you to establish the relationship between the visual component and the variable once, eliminating all the assignment statements from your code.

Chapter 1: Displaying Our Favorite Movie in a Form

We’ll create an app that shows information about your favorite movie. We’ll use data bindings to display data from a dictionary into Anvil components.

Step 1: Create an App

Log in to Anvil and click ‘Create a new app’. Choose the Material Design 3 theme.

Anvil’s new app screen showing options to create a new app, to start from a template, and to see examples.

That gives us an app with a Form named Form1.

Step 2: Add components to your form

We’ll build our UI by dragging-and-dropping components from the Toolbox into the page.

We want to display the movie name, the year it was released, the name of the director, and a summary of the movie’s plot.

Add eight Labels, four for the titles of what we’re displaying and four for the values to display. We’ll also add a Label into the title slot.

Animated GIF showing dropping labels onto the form to build the movie information form.

The final form should look something like this:

The final version of the movie information form with labels for the pieces of information and for where the actual information will go.

Step 3: Initialize data sources

Every Anvil component and Form has an item property, meant specifically for storing data of any kind. Our data source will be a dictionary that we’ll store in the item property of our Form.

Let’s go to the Editor’s Code view and create this dictionary in the __init__ function of the form:

class Form1(Form1Template):
def __init__(self, **properties):
self.item = {
'movie_name': 'Back to the Future',
'year': 1985,
'director': 'Robert Zemeckis',
'summary': 'Doc Brown invents a nuclear powered time machine, which Marty McFly '
'then uses to nearly erase himself from existence.'
}

# Set Form properties and Data Bindings.
self.init_components(**properties)

Note that the dictionary is created before the call to self.init_components. This is because the data bindings are processed in that call, so our data source (the self.item dictionary) must be set up before that.

Step 4: Set up data bindings

Now we need to tell Anvil what data to associate with which components (e.g. the data bindings). We’ll bind the data from our self.item dictionary to the text property of the Labels we just added.

To set up a data binding, click on the component that you want to have Anvil automatically set. I’ll start with the label that will hold the name of the movie.

You can find the text property at the top of the Properties Panel. We could type something in there but that would then always show that text. Instead, click the link icon to the right of the text property. This will cause the display to change and the link icon to turn blue, indicating that the data binding is active.

Set this data binding to the movie_name key of our dictionary:

We’ll now do the same for the other labels, using self.item['year'] for the year the movie was released, self.item['director'] for the director’s name, and self.item['summary'] for the plot summary.

After those are set up, we can double check that the data bindings are correct by looking at the Form’s Data Binding summary. You can find this by selecting your Form and scrolling to the bottom of the Properties Panel.

Step 5: Run the app

Your data bindings are all set! We’ve told the components which variables to pull data from to populate their text properties.

Run the app now and see the information about your movie displayed in the Form. We didn’t need to use any code to set the text properties of any of the Labels, they were all set through the data bindings we set up in the Labels’ text properties.

Chapter 2: Edit Movie Data

We’ll next allow the user to edit the movie data. For this, we’ll use a feature of data bindings called writeback. You can toggle data binding writeback for user-editable components. This means that when a user changes a property that has a data binding, the original variable will be updated with the new value.

Step 1: Create your edit form

Create a second Form in your app by clicking the three dots to the right of Client Code and choose Add Form. Select Blank Panel and rename it to MovieEdit.

Start by dropping a ColumnPanel into the page. Setting up the Form is very similar to what we did with our previous Form, but we’ll use TextBoxes instead of Labels, since we want the user to be able to edit the fields. Use a TextArea for the plot summary since that will usually be longer than a single line. This is how it should look:

Step 2: Set up data bindings

Setting up data bindings is much the same as before. Here’s the one for text_box_1, binding its text property to self.item['movie_name']:

In this case, though, notice that there’s a W at the right end of the data binding. That W stands for writeback and will show on any property that’s user-editable. Writeback defaults to inactive, but if you click the W you can activate it.

When writeback is active and the user edits the value in the Text Box, Anvil will update the original data binding source with those changes. It’s the equivalent of you executing self.item['movie_name'] = self.text_box_1.text when the TextBox value changes.

Since our purpose is to allow the user to edit the movie information, we want to enable writeback for all of these editable components. Set up the rest, and then check them in the Form’s Data Binding summary. They should look like this:

Note that we didn’t write any code in the MovieEdit Form. That’s the value of data bindings, to automate the boilerplate code of assigning values to visual components and then getting the values out when the user makes changes.

Step 3: Trigger your edit form

We want to access our MovieEdit Form from our main Form. We’ll do this by displaying the MovieEdit form in an alert every time a user clicks a button.

Add a Button on Form1. Set its text to ‘Edit movie information’ and change its name to edit_button.

We want some code to run when this Button is clicked. We’ll use events for this. Select the Button and click on ‘on click event’ in the Object Palette. This will open the Editor’s ‘Split’ view and automatically create an edit_button_click method associated with the button.

We’ll write the code that will run when we click edit_button inside this method. First, import the MovieEdit Form at the top of the Form:

from ..MovieEdit import MovieEdit

And then in the edit_button_click function itself create an instance of the MovieEdit Form:

def edit_button_click(self, **event_args):
editing_form = MovieEdit(item=self.item)

When we create the instance of the MovieEdit Form, we’re going to pass our dictionary of movie information as its item property. That will make it available in MovieEdit as self.item.

Now we’re going to display the Form in an alert. We’ll set the content of the alert to be editing_form and the large property to be True:

def edit_button_click(self, **event_args):
editing_form = MovieEdit(item=self.item)
alert(content=editing_form, large=True)

Run the app and click the editing button to see the movie information in the text boxes and text area. If any of the information doesn’t show up in a component, double check the data bindings.

Step 4: Read the changed values

The next step is to get the changed values back into the main Form. We are actually almost there since MovieEdit’s writeback data bindings are already changing Form1’s self.item dictionary. But Form1 doesn’t know that those values have changed, and so doesn’t know to rerun the data bindings to update the components.

We need to tell Form1 to update its data bindings after the alert returns. We’ll do this with self.refresh_data_bindings():

def edit_click(self, **event_args):
editing_form = MovieEdit(item=self.item)
alert(content=editing_form, large=True)
self.refresh_data_bindings()

Run the app now and edit the movie information to see it changed in Form1!

We can now edit our app’s movie data!

Chapter 3: Storing data in Data Tables

We can now edit our favorite movie data, but because it’s stored in a dictionary, it won’t persist when the app is re-run. Because of this, you’ll usually want to store data in a database when using data bindings.

In this chapter, we’ll change our app to store the movie data in a Data Table so that edits to the data will persist between runs of the app. Data Tables are an out-of-the-box option for data storage in Anvil, and they’re backed by PostgreSQL.

Step 1: Create a Data Table

In the Sidebar Menu, choose Data to open up your app’s Data Tables.

Next, click ‘+ Add Table’ to add a new Data Table to the app. Name the table ‘movies’.

With the new table selected, you’ll see the interface for editing the Data Table. We’ll set up our Data Table with 4 columns. We need to add the following columns:

  • movie_name (a text column)
  • year (a number column)
  • director (a text column)
  • summary (a text column).

We can now copy the data from the self.item dictionary and add it to our table.

Notice that we’ve used the same names for the columns as we used for the self.item keys, so that we do not need to change our data bindings.

Step 2: Configure the Data Table

Data tables live on the server, which is a secure and trusted environment. Client Forms and client code run in the user’s web browser, which the user has access to. Sufficiently motivated and knowledgeable users can modify the client code of any web app to make it do anything they’d like it to do, including removing any security checks you might put into client code.

By default Anvil Data Tables are only accessible from server code because this is the most secure option. You can see that in the Data Table interface:

You have two other options for client code. You can give client code read access (client code can search this table) or full access (client code can search, edit, and delete). Read access is appropriate for tables where there are no access restrictions needed on who can see data in the table. Full access is almost never appropriate for client code since it allows sufficiently motivated users to bypass all your security restrictions and all your validation code.

We’re going to use read access for our table since we only have the one row that we want all the users to be able to read. After making that change the configuration should look like this:

Step 3: Read from the Data Table

Now we need to change Form1 to use the movies Data Table as the source for self.item instead of our dictionary.

We can access any Data Table with app_tables.<data-table-name>. Since our table only has one row, we want to get the first row from the Data Table into self.item in the __init__ function for Form1.

class Form1(Form1Template):
def __init__(self, **properties):
self.item = app_tables.movies.search()[0]

# Set Form properties and Data Bindings.
self.init_components(**properties)

What app_tables.movies.search() returns looks a lot like a Python list, so we can use standard list indexing to pull the first item out of the list. What’s stored in the list are data table rows, which look enough like Python dictionaries to be used in self.item with data bindings.

You should now be able to run the app and see the information from the data table showing on the main page of your app. Editing doesn’t yet work, but that’s our next step to fix.

Chapter 4: Store edits to the Data Table

Because we are not allowing the client to write to our Data Table, we cannot use writeback as we have been.

Step 1: Update the Data Table

Instead, we’ll use a server function to update the Data Table.

Anvil’s Server Modules are a full server-side Python environment. Unlike client code, Server Modules cannot be edited or seen by the user, so we can trust them to do what we tell them. This is why we’ll use a Server Module to write to the Data Table.

For more information about the difference between client and server code, check out this explainer.

Let’s create a Server Module. In the App Browser, click the blue ‘+ Add Server Module’ button underneath ‘Server Code’. This will open up a code editor.

Our server function will be taking in a dictionary from the client and updating the Data Table with its contents. We’ll decorate it with @anvil.server.callable so that we can access it from client code. Remember that we’re still dealing with just the single row in the Data Table.

@anvil.server.callable
def update_movie(movie_data):
row = app_tables.movies.search()[0]
row['director'] = movie_data['director']
row['movie_name'] = movie_data['movie_name']
row['summary'] = movie_data['summary']
row['year'] = movie_data['year']

Server functions also give us a place to put any data validation we need. In the case of the movie data, we probably don’t want to allow any of the fields to be empty.

@anvil.server.callable
def update_movie(movie_data):
if movie_data['director'] and movie_data['movie_name'] and movie_data['summary'] and movie_data['year']:
row = app_tables.movies.search()[0]
row['director'] = movie_data['director']
row['movie_name'] = movie_data['movie_name']
row['summary'] = movie_data['summary']
row['year'] = movie_data['year']

The exact validation you do depends on your app and the server function, but it’s the spot to do last minute validation before you write changes to the Data Table.

Step 2: Make a copy of the Data Table row

Switch to the Code view for Form1 and look at the edit_click function.

We can no longer pass self.item directly to the MovieEdit form, since self.item is a read-only Data Table row and writeback will not work with it. What we can do instead is convert the item to a dictionary, effectively making a copy of it. Modify the edit_click function to do so.

def edit_click(self, **event_args):
item = dict(self.item)
editing_form = MovieEdit(item=item)
alert(content=editing_form, large=True)

Writeback data bindings will work fine on the dictionary, since it’s no longer a Data Table row, but a dictionary with copies of the values that were in the Data Table row.

Step 3: Update the Data Table row

After the alert is closed, we must call the server function to update the Data Table row and pass it the item dictionary. We do that by using anvil.server.call to call our update_movie server function.

def edit_click(self, **event_args):
item = dict(self.item)
editing_form = MovieEdit(item=item)
alert(content=editing_form, large=True)
anvil.server.call('update_movie', item)

Step 4: Update self.item

Now that the server function has been called, we must update the main Form’s data. We’ll reset self.item by getting the now updated row from our Data Table, then we’ll use self.refresh_data_bindings() to trigger the Form to update the text property of its components:

def edit_click(self, **event_args):
item = dict(self.item)
editing_form = MovieEdit(item=item)
alert(content=editing_form, large=True)
anvil.server.call('update_movie', item)
self.item = app_tables.movies.search()[0]
self.refresh_data_bindings()

Now, edits to the app will persist when you re-run the app. All users will see the edits that any users make.

Run the app, edit the movie information, and then stop the app and verify the data in the Data Tables has changed.

And that’s it! We have built a simple app to showcase data for your favorite movie. From here, you could extend your app’s functionality, for example by allowing users to have their own separate favorite movies.

What’s Next?

Head to the Anvil Learning Centre for more guided tutorials, or head to our examples page to see the variety of apps you can build with Anvil.

--

--

Jay Shaffstall
Anvil
Writer for

Father, educator, software developer. Python and Anvil enthusiast.