Data Loading

Most apps will not need to use the load_data method and will load data during the form's instantiation or pass data through form properties. Many of the advantages of data loading can be achieved by using cached forms.

An advantage of the data loading mechanism, over loading data during form instantiation, is that it allows data to be sent from the server during the initial page request. However, since the routing library takes advantage of client-side routing after the initial page request, the advantages of data loading are limited to the first page request.

Limitations

Data caching is determined by the path and the dictionary returned by the cache_deps method. If your App needs to share data between routes, you may find that data caching is not sufficient and results in duplicate data being loaded. You can mitigate duplicate data by using form_properties or nav_context for simple data sharing.

Example

Without a load_data method

# routes.py
from routing.router import Route

Route.cache_form = True

class ArticleRoute(Route):
    path = "/articles/:id"
    form = "Pages.Article"
from routing import router

class ArticleForm(ArticleFormTemplate):
    def __init__(self, routing_context: router.RoutingContext, **properties):
        self.routing_context = routing_context
        if properties.get("item") is None:
            # The user navigated directly to the form by changing the URL
            properties["item"] = anvil.server.call("get_article", routing_context.params["id"])

        self.init_components(**properties)

In the above example, if a user goes directly to the URL /articles/123, the initial page request will send the user to the ArticleForm, but there will be no data. The App will then need to make a server call to get the data.

Note that during normal navigation, i.e. when the user clicks a link, we can take advantage of the form_properties attribute to ensure we do not load unnecessary data.

class RowTemplate(RowTemplateTemplate):
    def __init__(self, **properties):
        self.init_components(**properties)

    def on_button_click(self, **event_args):
        router.navigate(
            path="/articles/:id",
            params={"id": self.item["id"]},
            form_properties={"item": self.item}
        )

With a load_data method

# routes.py
from routing.router import Route

class ArticleRoute(Route):
    path = "/articles/:id"
    form = "Pages.Article"

    def load_data(self, **loader_args):
        row = loader_args.nav_context.get("row")
        if row is None:
            id = loader_args["path_params"]["id"]
            row = anvil.server.call("get_row", id)
        return row
from routing import router

class ArticleForm(ArticleFormTemplate):
    def __init__(self, routing_context: router.RoutingContext, **properties):
        self.routing_context = routing_context
        properties["item"] = routing_context.data
        self.init_components(**properties)

In the above example, the load_data is called whenever the user navigates. If a user navigates directly to the URL /articles/123, the initial page request will come in, the load_data method will be called (on the server), and the user will be directed to the ArticleForm with the data already loaded. During normal navigation, i.e. when the user clicks a link, we can take advantage of the nav_context (or form_properties) attribute to ensure we do not make unnecessary server calls during client-side navigation.

class RowTemplate(RowTemplateTemplate):
    def __init__(self, **properties):
        self.init_components(**properties)

    def on_button_click(self, **event_args):
        router.navigate(
            path="/articles/:id",
            params={"id": self.item["id"]},
            nav_context={"row": self.item}
        )

Invalidating Data

See Invalidating Cache.

Pending Form

When data is loading for the first time, a user can provide a loading form. This form will be shown while the data is loading.

The pending form is determined by the Route.pending_form attribute. When the data is loading, the routing library will wait for the pending_delay seconds before showing the pending form. It will show the pending form for at least pending_min seconds.

from routing.router import Route

class ArticleRoute(Route):
    path = "/articles/:id"
    form = "Pages.Article"
    pending_form = "Pages.Loading"
    pending_delay = 1 # default is 1
    pending_min = 0.5 # default is 0.5

A common implementation will be to create a pending form with the same layout as the form. Where the content would be, place an Anvil.Spacer component. Inside the show and hide event handlers, call the anvil.server.loading_indicator.start and anvil.server.loading_indicator.stop functions.

from anvil.server import loading_indicator

class LoadingForm(LoadingFormTemplate):
    def __init__(self, **properties):
        self.init_components(**properties)
        self.loading_indicator = anvil.server.loading_indicator(self.spacer_1)

    def show(self, **event_args):
        self.loading_indicator.start()

    def hide(self, **event_args):
        self.loading_indicator.stop()