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.

Navigation

An Anvil app is composed of Forms, and navigation is simply a matter of displaying the correct Form.

Displaying a new page

To display a new page, call open_form() (from the anvil module), and pass in the instance of the Form that you’d like to display as your new page. When you do this, the new Form takes over entirely and becomes the top-level Form. The old Form is no longer visible.

If you pass any extra parameters into open_form(), they are passed to the new Form’s constructor:

class Form1(Form1Template):
  # ...

  def btn1_click(self, **event_args):
    open_form('Form2', my_parameter="an_argument")

As well as specifying a Form by name, you can also create an instance of the Form yourself and pass it in. This code snippet is equivalent to the previous one:

from Form2 import Form2

class Form1(Form1Template):
  # ...

  def btn1_click(self, **event_args):
    frm = Form2(my_parameter="an_argument")
    open_form(frm)
Note that Forms are not available to other Forms unless you explicitly import them. Each Form has its own module, which has the same name as the Form, so to import Form2 from inside Form1, you use from Form2 import Form2.

Replacing just the main content

Since Forms can be contained inside other Forms, you don’t need to throw away all of your current Form in order to display a new Form. For example, you might have a top-level Form with a menu bar that needs to be visible all the time, and display all other app content as panels within that page.

An app with a nav bar that replaces just the main content.

An app with a nav bar that replaces just the main content.

You can use get_open_form() to get a reference to the currently-open Form object. You can then use that to change what is displayed in the top-level Form, even from deep within a nested structure of Forms and components.

Just clear the contents from the main container on the Form, and instantiate a new Form inside that container:

# import Form2 to make it available inside the top-level Form
from Form2 import Form2

new_panel = Form2()

# The top-level form has a component called
# column_panel. Clear it and put a new Form2() panel there:
get_open_form().content_panel.clear()
get_open_form().content_panel.add_component(new_panel)

Creating a nav bar

A common way to handle navigation is to use a nav bar. You can do this by creating a set of Links in the top-level Form and using them to switch the main contents as described above.

Here’s a worked example of how to do that, and some tips.

Worked example

In the Material Design theme, you can drop a Column Panel ColumnPanel icon into the sidebar and drop Link components into that Column Panel.

Each Link can have a ‘click’ event handler to manage navigation in your app by running the code from either example above when a Link is clicked.

Creating an event handler for the ‘Page 1’ link.

Creating an event handler for the ‘Page 1’ link.

In the example below, Form1 is an app with a navbar, and Page1 and Page2 are ‘content’ Forms that you want to switch between using the navbar. Each click handler inserts the relevant ‘content’ Form into Form1 when the link is clicked:

# import Page1 to make it available inside Form1
from Page1 import Page1
# import Page2 to make it available inside Form1
from Page2 import Page2

class Form1(Form1Template):
  # ...

  def link_1_click(self, **event_args):
    # Clear the content panel
    self.content_panel.clear()
    # Add Page1 to the content panel
    self.content_panel.add_component(Page1())

  def link_2_click(self, **event_args):
    # Clear the content panel
    self.content_panel.clear()
    # Add Page2 to the content panel
    self.content_panel.add_component(Page2())

This will replace the main content of the page each time a Link is clicked.

You can change the look of the Link that was clicked to show the user which page they’re on. For example, in the Material Design theme you can set the Link’s role to selected:

from Page1 import Page1
from Page2 import Page2

class Form1(Form1Template):
  # ...

  def reset_links(self, **event_args):
    self.link_1.role = ''
    self.link_2.role = ''

  def link_2_click(self, **event_args):
    # Set link_2 to look 'selected'
    self.reset_links()
    self.link_2.role = 'selected'

    # Add Page2 to the main panel
    self.content_panel.clear()
    self.content_panel.add_component(Page2())

You don’t have to write one click handler per Link. You can get which Link has been clicked from the event_args['sender'], so you can use that to work out which Form to instantiate.

Say, for example, your app has top-level Form which contains three navigation links called home_link, about_link, and contact_link, as well as three additional Forms called ‘Home’, ‘About’ and ‘Contact’. You can set each Link’s tag attribute to the name of the Form you want to open (or an instance of that Form):

from Home import Home
from About import About
from Contact import Contact

# set each Link's `tag.form_to_open` attribute to an instance of the Form you want to open
self.home_link.tag.form_to_open = Home()
self.about_link.tag.form_to_open = About()
self.contact_link.tag.form_to_open = Contact()

Then, you can configure one click handler to use for home_link, about_link, and contact_link in your top-level Form:

  def nav_link_click(self, **event_args):
    """A generalised click handler that you can bind to any nav link."""
    # Find out which Form this Link wants to open
    form_to_open = event_args['sender'].tag.form_to_open

    self.content_panel.clear()
    self.content_panel.add_component(form_to_open)

You can then set the nav_link_click function as the ‘click’ event handler for home_link, contact_link and about_link:

Apply one click handler to all nav links

Apply one click handler to all nav links

Using the URL hash

get_url_hash() gets the decoded hash (the part after the ‘#’ character) of the URL used to open this app.

self.label_1.text = "Our URL hash is: %r" % get_url_hash()

You can create a URL-based navigation system by opening a particular Form depending on the URL hash:

if get_url_hash() == 'stats':
  from Stats import Stats
  self.content_panel.clear()
  self.content_panel.add_component(Stats())
elif get_url_hash() == 'analysis':
  from Analysis import Analysis
  self.content_panel.clear()
  self.content_panel.add_component(Analysis())

Query params

If the first character of the hash is a question mark (e.g. https://myapp.anvil.app/#?a=foo&b=bar), it will be interpreted as query-string-type parameters and returned as a dictionary (e.g. {'a': 'foo', 'b': 'bar'}).

get_url_hash() is available in Form code only.