Live Chat

We'll need to share your messages (and your email address if you're logged in) with our live chat provider, Drift. Here's their privacy policy.

If you don't want to do this, you can email us instead at contact@anvil.works.

Data Grids - Paginated Grouping

In the Data Grids Getting Started tutorial, we created a simple paginated table that presented employee data, and we tweaked the settings to get it how we wanted it.

Now let’s explore the power of the nested structure of Data Grids.

Imagine you want to show a table where the employees are grouped by team, and each team has a heading within the table. Something like this:

We’ll start with the original flat table, and modify it to implement the grouping behaviour.

Starting point

Clone the basic Data Grids example app to follow along with this tutorial - it’s the starting point you’ll build from.

Clone Starting Point


To make this easy, you should store the team names in their own Data Table. So, add a table called ‘teams’ with a single text column called name:

Then change the team column of the ‘employees’ table to link to it:

With the database schema changed, you need to regenerate the data. The example app has a Server Module called RandomEmployeeGenerator to create the data for you. Just call the server function generate_normalised the first time you ever run the app:

class Form1(Form1Template):
  def __init__(self, **properties):
    # ... after the normal init stuff ...
    anvil.server.call('generate_normalised')

Run the app once to populate the database. Then remove the call so it doesn’t run every time!

A repeating panel inside a repeating panel

You’re going to group the employees into teams. So you want the Data Grid to be made of sub-tables, with one sub-table per team.

Each row of the Data Grid will be a single team. Then within each team-row, you’re going to add a Repeating Panel to list the employees in that team.

So the first thing to do is set the Data Grid’s items to be the contents of the ‘team’ table, rather than ‘employees’. Remember that a Data Grid contains a Repeating Panel; let’s rename it repeating_panel_teams. In the code you need to call a server function to get the teams data, where we were previously getting the employee data:

  def __init__(self, **properties):
    # ... after the normal init stuff ...
    self.repeating_panel_teams.items = anvil.server.call('get_teams')

The get_teams function on the server side is simply a Data Tables search:

def get_teams():
  return app_tables.teams.search()

Now let’s modify the template of the Repeating Panel so that each row is a roster for a particular team. Double-click the Repeating Panel to edit the item template. The row should now be highlighted, with the rest of the screen greyed out.

Uncheck the auto_display_data box in the Properties panel - this removes the Data Row Panel that’s included by default, leaving an empty space where you can add whatever you like.

First, add a label to display the team name . Now your Data Grid will be just a list of team names, like this:

Go back editing the Repeating Panel’s item template. Add another Repeating Panel, to hold the list of employees for this team:

That’s what produces the grouped structure. Let’s call the inner Repeating Panel repeating_panel_employees, since that’s what it’ll hold. The overall structure now looks like this:

The outer Repeating Panel, repeating_panel_teams, has one instance of RowTemplate1 for each team. Each of these instances of RowTemplate1 has a Repeating Panel of its own, called repeating_panel_employees. For each employee in that team, there is an instance of RowTemplate2. Also shown on the diagram are the Data Row Panel containing the custom column headers, and the Column Panel containing the page size selector.

Setting up the employee repeating panel

Now let’s get the correct data into repeating_panel_employees. You can get the relevant list of employees when the panel initialises.

Write a server function to get the employees in a particular team:

@anvil.server.callable
def get_employees_in_team(team):
  return app_tables.employees.search(team=team)

And call this in the __init__ method for RowTemplate1 (RowTemplate1 is a separate Form, so to edit its code, click on it in the Forms section of the App Browser (left-hand panel) and click on ‘Code’.)

class RowTemplate1(RowTemplate1Template):
  def __init__(self, **properties):
    # ... after the normal init stuff ...
    self.repeating_panel_employees.items = anvil.server.call(
      'get_employees_in_team',
      self.item,
    )

This preserves the lazy loading behaviour of the Data Grid - every time a page is loaded, only the data for the teams that can be seen on the screen is fetched.

You want repeating_panel_employees to have two columns; employee name and grade. So let’s delete the team column from the Data Grid. Then, put a label into the leftmost column to display employee name, as we did in the Data Grids Getting Started tutorial.

Set up a data binding in repeating_panel_employees.label_employee_name to include both first and last name:

self.item is a row from the Employees table, because you set repeating_panel_employees.items to the results of a search on the Employees table earlier.

The internal Repeating Panel will now show the employee data as two columns: employee name and grade.

And you’re done

Hit run and watch your handiwork in action - you should get a table of employees, grouped by team, with each team headed by a label telling you which team it is:

Note that the rows_per_page of the Data Grid is still honoured, even though we’ve got a nested structure of Repeating Panels. Any Data Row Panel that descends from a Data Grid will be aware of the rows_per_page of its ancestor.

Here’s a clone link for the final app:

Clone Final App


Feel free to use it and share it - our code examples are open-source under the Apache License.

Here are our other tutorials on Data Grids: