### Install and Serve Documentation
Source: https://github.com/anvil-works/routing/blob/master/README.md
Use these commands to install dependencies and serve the documentation locally. Ensure you activate the virtual environment before running the commands.
```bash
pip install uv
uv venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt
mkdocs serve
```
--------------------------------
### Install Pre-commit
Source: https://github.com/anvil-works/routing/blob/master/docs/contributing/index.md
Install the pre-commit tool to ensure code quality and consistency before committing.
```bash
pip install pre-commit
```
--------------------------------
### Set up MkDocs Documentation Environment
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Install the necessary tools and dependencies for building and previewing the project documentation locally using MkDocs.
```bash
pip install uv
uv venv
source .venv/bin/activate
```
```bash
uv pip install -r requirements.txt
```
--------------------------------
### Install Git Hooks
Source: https://github.com/anvil-works/routing/blob/master/docs/contributing/index.md
Install the pre-commit git hooks. These checks will run automatically on commit.
```bash
pre-commit install
```
--------------------------------
### Install and Configure Pre-commit Hooks
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Install pre-commit and its git hooks to maintain code quality. These hooks run automatically before each commit.
```bash
pip install pre-commit
```
```bash
pre-commit install
```
--------------------------------
### Route Ordering Example
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Demonstrates the importance of route order. More specific routes, like `/authors/new`, should be defined before more general routes, like `/authors/:id`, to ensure correct matching.
```python
from routing.router import Route
class AuthorsRoute(Route):
path = "/authors"
form = "Pages.Authors"
class NewAuthorRoute(Route):
path = "/authors/new"
form = "Pages.NewAuthor"
class AuthorRoute(Route):
path = "/authors/:id"
form = "Pages.Author"
```
--------------------------------
### URL and Context Utilities
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Functions for getting URLs, routing contexts, and debugging.
```APIDOC
## URL and Context Utilities
### `get_routing_context()`
**Description**: Returns the current routing context.
### `get_url()`
### `get_url(*, path=None, params=None, query=None, hash=None, full=False)`
### `get_url(path, **kws)`
### `get_url(routing_context, **kws)`
**Description**: Gets the URL. If no keyword arguments are passed, the current URL will be returned. If `full` is `True`, the full URL will be returned (e.g., `http://my-app.anvil.app/articles/123?foo=bar#hash`). If `full` is `False`, the URL will be relative to the base URL (e.g., `/articles/123?foo=bar#hash`).
### `debug_logging(enable=True)`
**Description**: Enables or disables debug logging.
```
--------------------------------
### Implement Authentication and Redirects with before_load Hooks
Source: https://context7.com/anvil-works/routing/llms.txt
Use the `@hooks.before_load` decorator to run checks before a route loads. This example demonstrates authentication, permission checks, and redirects to login or suspension pages.
```python
from routing.router import Route, hooks, Redirect
class AuthenticatedRoute(Route):
"""Base route that requires authentication"""
@hooks.before_load
def check_auth(self, nav_context, **loader_args):
user = anvil.users.get_user()
if not user:
raise Redirect(path="/login", query={"next": loader_args["path"]})
nav_context["user"] = user
@hooks.before_load
def check_permissions(self, nav_context, **loader_args):
user = nav_context.get("user")
if user and not user["is_active"]:
raise Redirect(path="/account-suspended")
class AdminRoute(AuthenticatedRoute):
"""Route that requires admin access"""
@hooks.before_load
def check_admin(self, nav_context, **loader_args):
user = nav_context.get("user")
if not user.get("is_admin"):
raise Redirect(path="/unauthorized")
class DashboardRoute(AuthenticatedRoute):
path = "/dashboard"
form = "Pages.Dashboard"
class AdminPanelRoute(AdminRoute):
path = "/admin"
form = "Pages.AdminPanel"
# Global hook applied to all routes
@hooks.before_load
def add_analytics(self, nav_context, **loader_args):
nav_context["session_id"] = get_session_id()
Route.add_analytics = add_analytics
```
--------------------------------
### Conditional Redirect based on User Login
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/redirecting.md
Implement conditional redirects in `before_load` to guide users based on their login status. This example redirects logged-in users to '/dashboard' and others to '/login'.
```python
from routing.router import Route, Redirect
import anvil.users
class IndexRoute(Route):
path = "/"
def before_load(self, **loader_args):
if anvil.users.get_user():
raise Redirect(path="/dashboard")
else:
raise Redirect(path="/login")
```
--------------------------------
### Basic Redirect in before_load
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/redirecting.md
Use `Redirect` in `before_load` to send users to a different route upon navigation. This example redirects all traffic from '/' to '/dashboard'.
```python
from routing.router import Route, Redirect
class IndexRoute(Route):
path = "/"
def before_load(self, **loader_args):
raise Redirect(path="/dashboard")
```
--------------------------------
### Clone Routing Repository
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Clone your forked repository locally to start making changes. Ensure you replace YOUR-USERNAME with your GitHub username.
```bash
git clone https://github.com/YOUR-USERNAME/routing.git
```
--------------------------------
### Construct URLs with URL Utilities
Source: https://context7.com/anvil-works/routing/llms.txt
Generate current or full URLs using `get_url()`. Construct URLs for specific routes by providing a path and parameters. Access routing context to get URLs within forms.
```python
from routing import router
# Get current URL
current_url = router.get_url() # e.g., "/articles/123?tab=comments"
full_url = router.get_url(full=True) # e.g., "https://myapp.anvil.app/articles/123?tab=comments"
# Construct URL for a specific route
article_url = router.get_url(path="/articles/:id", params={"id": 456})
# Returns: "/articles/456"
# Get URL from routing context
class ShareButton(ShareButtonTemplate):
def __init__(self, routing_context, **properties):
self.routing_context = routing_context
self.init_components(**properties)
def share_click(self, **event_args):
share_url = self.routing_context.get_url(full=True)
# Copy to clipboard or share
```
--------------------------------
### Implement Loading Indicator in Pending Form
Source: https://github.com/anvil-works/routing/blob/master/docs/data-loading/index.md
Use `anvil.server.loading_indicator` within a pending form to visually indicate that data is loading. Ensure the indicator is started in `show` and stopped in `hide`.
```python
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()
```
--------------------------------
### TemplateWithContainerRoute Basic Setup
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/forms.md
Use `TemplateWithContainerRoute` for traditional Anvil routing where forms are added to a specific template container. Ensure the `BaseRoute.template` is set.
```python
from routing.router import TemplateWithContainerRoute as BaseRoute
BaseRoute.template = "MainTemplate"
class IndexRoute(BaseRoute):
path = "/"
form = "Pages.Index"
```
--------------------------------
### Define Routes for Partial Invalidation Example
Source: https://github.com/anvil-works/routing/blob/master/docs/caching/index.md
Define multiple `Route` subclasses with overlapping paths to demonstrate partial cache invalidation. The `invalidate` function's `exact` parameter controls whether subsets are included.
```python
from routing.router import Route
class ArticlesRoute(Route):
path = "/articles"
form = "Pages.Articles"
class ArticleRoute(Route):
path = "/articles/:id"
form = "Pages.Article"
```
--------------------------------
### Serve MkDocs Locally
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Build and serve the documentation website locally to preview changes. Access the preview at http://127.0.0.1:8000.
```bash
mkdocs serve
```
--------------------------------
### Launch the routing library
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Call this function in your startup module to initialize the routing library and navigate to the initial route defined in your application.
```python
launch()
```
--------------------------------
### Get current routing context
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Retrieves the current routing context, which contains information about the active route and its associated data.
```python
get_routing_context()
```
--------------------------------
### Launch the Router in Anvil
Source: https://context7.com/anvil-works/routing/llms.txt
Initialize the routing library and navigate to the first matching route. Call this in your startup module.
```python
from routing.router import launch
if __name__ == "__main__":
launch()
```
--------------------------------
### Configure Template Container Properties
Source: https://github.com/anvil-works/routing/blob/master/docs/migrating/index.md
Set `template_container_properties` on a specific route to configure properties for its template container. This example sets `full_width_row` to `True`.
```python
from routing.router import TemplateWithContainerRoute as BaseRoute
BaseRoute.template = "MainTemplate"
class IndexRoute(BaseRoute):
path = "/"
form = "Pages.Index"
template_container_properties = {"full_width_row": True}
```
--------------------------------
### Initialize Form with RoutingContext
Source: https://github.com/anvil-works/routing/blob/master/docs/routing-context/index.md
Instantiate a form and pass the RoutingContext to it. This allows the form to access routing information and participate in navigation events.
```python
from ._anvil_designer import IndexTemplate
from routing.router import RoutingContext
class IndexTemplate(IndexTemplate):
def __init__(self, routing_context: RoutingContext, **properties):
self.routing_context = routing_context
self.init_components(**properties)
```
--------------------------------
### Create NavLinks and Anchors for Navigation
Source: https://context7.com/anvil-works/routing/llms.txt
Dynamically create NavLink components for menus and Anchor components for inline links. Configure active state tracking with `exact_path`, `exact_query`, and parameters.
```python
from routing.router import NavLink, Anchor
class MainLayout(MainLayoutTemplate):
def __init__(self, **properties):
self.init_components(**properties)
# Create NavLinks programmatically
self.sidebar.add_component(
NavLink(
text="Home",
path="/",
# exact_path=True is automatic for "/"
)
)
self.sidebar.add_component(
NavLink(
text="Articles",
path="/articles",
exact_path=False, # Active for /articles, /articles/123, etc.
)
)
self.sidebar.add_component(
NavLink(
text="Settings",
path="/settings",
query={"tab": "profile"},
exact_query=True, # Only active when query matches exactly
)
)
# NavLink with params
self.add_component(
NavLink(
text="View Article",
path="/articles/:id",
params={"id": self.item["id"]},
nav_context={"article": self.item}, # Pass data to avoid reload
)
)
class ArticleContent(ArticleContentTemplate):
def __init__(self, **properties):
self.init_components(**properties)
# Anchor for inline links
self.content.add_component(
Anchor(
text="Read more about Python",
path="/topics/python",
)
)
```
--------------------------------
### Error Form Implementation
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
An example `Error` form that displays routing context information. It re-raises the original error in debug environments to aid in debugging.
```python
# Pages.Error
import anvil
class Error(ErrorTemplate):
def __init__(self, routing_context: RoutingContext, **properties):
self.init_components(**properties)
self.routing_context = routing_context
self.label.text = (
f"Error when navigating to {routing_context.path!r}, got {routing_context.error!r}"
)
def form_show(self, **event_args):
if anvil.app.environment.name.startswith("Debug"):
raise self.routing_context.error
```
--------------------------------
### Launch Anvil Router
Source: https://github.com/anvil-works/routing/blob/master/docs/quick-start.md
This code launches the Anvil router. It should be placed in the startup module.
```python
# startup.py
from routing.router import launch
if __name__ == "__main__":
launch()
```
--------------------------------
### Navigate with Path Parameters
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/params.md
Navigate to a route with parameters by passing a dictionary to the `params` argument of the `navigate` function. The parameter values will be automatically appended to the URL.
```python
from routing.router import navigate
...
def button_click(self, **event_args):
navigate(path="/authors/:id", params={"id": 123})
```
--------------------------------
### Define Routes using Route.create() Convenience Method
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Use the Route.create() static method as a more concise way to define routes, directly providing the path and form. This is useful for simpler route definitions.
```python
from routing.router import Route
IndexRoute = Route.create(path="/", form="Pages.Index")
AboutRoute = Route.create(path="/about", form="Pages.About")
ContactRoute = Route.create(path="/contact", form="Pages.Contact")
```
--------------------------------
### Define Routes using Route Class Inheritance
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Define application routes by inheriting from the Route class and specifying the path and form for each route. This is the traditional way to set up routes.
```python
from routing.router import Route
class IndexRoute(Route):
path = "/"
form = "Pages.Index"
class AboutRoute(Route):
path = "/about"
form = "Pages.About"
class ContactRoute(Route):
path = "/contact"
form = "Pages.Contact"
```
--------------------------------
### Navigate with Nav Context
Source: https://github.com/anvil-works/routing/blob/master/docs/data-loading/index.md
When navigating, pass data using `nav_context` to be available in the `load_data` method. This allows the `load_data` method to potentially skip a server call if the data is already provided.
```python
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}
)
```
--------------------------------
### Get the current or a generated URL
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Generates a URL based on the provided path, parameters, query, and hash. If `full=True`, returns the absolute URL; otherwise, returns a relative URL. Without arguments, it returns the current URL.
```python
get_url()
```
```python
get_url(*, path=None, params=None, query=None, hash=None, full=False)
```
```python
get_url(path, **kws)
```
```python
get_url(routing_context, **kws)
```
--------------------------------
### Register Links with Multiple Exact Matching Options
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/navigation-components.md
Supports combining `data-exact-path`, `data-exact-query`, and `data-exact-hash` attributes for precise link matching. This allows for granular control over when a link is considered active based on the URL, query parameters, and hash. Store the cleanup function.
```python
# In your HTML template, you can use multiple data attributes (presence-based):
# All Articles
# Article Comments
# Full Match
cleanup = router.register_links(
self.dom_nodes["nav"],
active_class="active"
)
```
--------------------------------
### Define Route with Path Parameter
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/params.md
Define a route with a path parameter using the ':' syntax. The parameter will be available in the routing context.
```python
from routing.router import Route
class AuthorRoute(Route):
path = "/authors/:id"
form = "Pages.Author"
```
--------------------------------
### Override cache_deps to Control Form Caching
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/query.md
Override the `cache_deps` method to return a dictionary of dependencies that control when a form and its data are loaded from cache. This example shows a form cached uniquely by its `path`, ignoring changes in `query` parameters like `tab`.
```python
from routing.router import Route
class DashboardRoute(Route):
path = "/dashboard"
form = "DashboardForm"
cache_form = True
def cache_deps(self, **loader_args):
# this form is cached uniquely by the `path` only - there are no `query` dependencies
# i.e. if the `tab` changes, we keep the same instance of the form
return None
```
--------------------------------
### Navigate with Query Parameters
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/query.md
Pass query parameters as a decoded Python dictionary to the navigate function. The library handles encoding.
```python
from ._anvil_designer import DashboardTemplate
from routing.router import navigate, RoutingContext
class Dashboard(DashboardTemplate):
def __init__(self, routing_context: RoutingContext, **properties):
self.init_components(**properties)
self.routing_context = routing_context
routing_context.add_event_handler("query_changed", self.on_query_change)
routing_context.raise_init_events() # raises the query_changed event
def on_query_change(self, **event_args):
query = self.routing_context.query
self.tab_1.value = query.get("tab", "income")
def tab_changed(self, **event_args):
tab_value = self.tab_1.value
navigate(query={"tab": tab_value}) # Pass decoded dict with proper types
```
--------------------------------
### Build Nav Context Sequentially with Hooks
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
Hooks are called in order, allowing each hook to build upon the `nav_context` modified by previous hooks. This enables composable navigation logic.
```python
from routing.router import Route, hooks, Redirect, Redirect
class AuthenticatedRoute(Route):
@hooks.before_load
def set_user(self, nav_context, **loader_args):
nav_context["user"] = get_current_user()
@hooks.before_load
def check_permissions(self, nav_context, **loader_args):
user = nav_context.get("user")
if not user or not user.has_permission():
raise Redirect(path="/login")
class FeatureRoute(AuthenticatedRoute):
@hooks.before_load
def add_feature_flag(self, nav_context, **loader_args):
nav_context["feature_enabled"] = True
```
--------------------------------
### Using `form_properties` for navigation
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
Demonstrates how to pass `form_properties` during navigation, which is useful for initializing a form with specific data, such as the selected item from a list.
```APIDOC
## POST /navigate with form_properties
### Description
Navigates to a new route and passes specific properties to the target form, allowing for pre-initialization.
### Method
POST
### Endpoint
/navigate
### Parameters
#### Request Body
- **path** (string) - Required - The destination path.
- **params** (object) - Optional - Path parameters.
- **form_properties** (object) - Required - A dictionary of properties to be passed to the target form's `__init__` method.
### Request Example
```python
# In the source form
router.navigate(
path="/articles/:id",
params={\"id\": self.item[\"id\"]},
form_properties={"item": self.item}
)
# In the target form (/articles/:id)
class ArticleForm(ArticleFormTemplate):
def __init__(self, routing_context: router.RoutingContext, **properties):
self.routing_context = routing_context
if properties.get("item") is None:
# Handle direct navigation
article_id = routing_context.params["id"]
properties["item"] = anvil.server.call("get_article", article_id)
self.init_components(**properties)
```
```
--------------------------------
### Navigate to a specific path with parameters
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
Use the `navigate` function to go to a specific route, passing dynamic parameters. This is typically called from an event handler like a button click.
```python
from routing.router import navigate
class Form(FormTemplate):
def nav_button_click(self, **event_args):
navigate(path="/articles/:id", params={"id": 123})
```
--------------------------------
### Server Route Generation Logic
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Illustrates the pseudo-code logic Anvil uses to automatically import routes and create server route handlers. This code is for conceptual understanding only.
```python
# ServerRoutes.py
from . import routes
# pseudo code - for illustration only
for route in Routes.__subclasses__():
if route.path is None:
continue
@anvil.server.route(route.path)
def server_route(**params):
...
# return a response object that will open the form on the client
```
--------------------------------
### Use get_app_origin() for Absolute Meta URLs
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/meta.md
Construct absolute URLs for meta tags, particularly Open Graph images, by using the `get_app_origin()` utility. This is recommended for proper social sharing previews.
```python
from routing import Route, get_app_origin
class AboutRoute(Route):
path = "/about"
form = "Pages.About"
def meta(self, **kwargs):
origin = get_app_origin()
meta_data = {
'title': "About Us",
'description': "Information about our company and team.",
'og:title': "About Our Company",
'og:description': "Discover our mission, values, and team members.",
'og:image': f"{origin}/_/theme/about.jpeg" # Absolute URL
}
return meta_data
```
--------------------------------
### Navigate Programmatically with Anvil's navigate()
Source: https://context7.com/anvil-works/routing/llms.txt
Programmatically navigate to routes using paths, parameters, query parameters, and hash fragments. Supports updating query parameters and replacing the current URL.
```python
from routing.router import navigate
class MyForm(MyFormTemplate):
def __init__(self, **properties):
self.init_components(**properties)
# Navigate to a simple path
def home_click(self, **event_args):
navigate(path="/")
# Navigate with path parameters
def view_article_click(self, **event_args):
navigate(path="/articles/:id", params={"id": 123})
# Navigate with query parameters
def filter_click(self, **event_args):
navigate(path="/articles", query={"category": "tech", "page": 1})
# Update query params using a function (preserves existing params)
def toggle_sidebar_click(self, **event_args):
def update_query(prev):
return {**prev, "sidebar": not prev.get("sidebar", False)}
navigate(query=update_query)
# Navigate with form_properties (passed to the form's __init__)
def row_click(self, **event_args):
navigate(
path="/articles/:id",
params={"id": self.item["id"]},
form_properties={"item": self.item}
)
# Replace current URL instead of pushing to history
def redirect_click(self, **event_args):
navigate(path="/dashboard", replace=True)
```
--------------------------------
### Raise Initialization Events
Source: https://github.com/anvil-works/routing/blob/master/docs/routing-context/index.md
Manually triggers initialization events like `data_loaded`, `query_changed`, and `hash_changed`. Useful after setting up event handlers during form instantiation.
```python
routing_context.raise_init_events()
```
--------------------------------
### Navigate Using Route Class Path
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/params.md
Navigate to a route by referencing its path directly from the route class. This is useful for maintaining consistency and avoiding hardcoded strings.
```python
from routing.router import navigate
from ...routes import AuthorRoute
navigate(path=AuthorRoute.path, params={"id": 123})
```
--------------------------------
### Navigate to a new page
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Use this function to navigate to a different page within the application. It supports specifying the path, parameters, query, and hash. Optional arguments allow for replacing the current history entry or specifying a navigation context.
```python
navigate(*, path=None, params=None, query=None, hash=None, replace=False, nav_context=None, form_properties=None)
```
```python
navigate(path, **kws)
```
```python
navigate(url, **kws)
```
```python
navigate(routing_context, **kws)
```
--------------------------------
### Using `nav_context` for navigation
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
Explains how to use `nav_context` to pass contextual information, such as the previous routing context, to a new route. This is helpful for preserving state or enabling back navigation with context.
```APIDOC
## POST /navigate with nav_context
### Description
Navigates to a new route while passing contextual data, such as the previous routing context, which can be used for state preservation or contextual actions.
### Method
POST
### Endpoint
/navigate
### Parameters
#### Request Body
- **path** (string) - Required - The destination path.
- **nav_context** (object) - Required - A dictionary containing contextual information to be passed to the next route.
### Request Example
```python
# In the source form
current_context = router.get_routing_context()
router.navigate(path="/foo", nav_context={"prev_context": current_context})
# In the target form (/foo)
class FooForm(FooFormTemplate):
def __init__(self, routing_context: router.RoutingContext, **properties):
self.routing_context = routing_context
self.init_components(**properties)
def cancel_button_click(self, **event_args):
prev_context = self.routing_context.nav_context.get("prev_context")
if prev_context is not None:
router.navigate(prev_context)
else:
router.navigate(path="/")
```
```
--------------------------------
### Route Class Definition
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Defines how to create routes using the Route class, either by subclassing or using the create() method.
```APIDOC
## Route Class Definition
### Description
The `Route` class is used to define routes for your app. When a user navigates to a path, the router will look for a matching route. The router will call `anvil.open_form` on the matching route's form.
### Subclassing Route
```python
# routes.py
from routing.router import Route
class IndexRoute(Route):
path = "/"
form = "Pages.Index"
class AboutRoute(Route):
path = "/about"
form = "Pages.About"
class ContactRoute(Route):
path = "/contact"
form = "Pages.Contact"
```
### Using Route.create()
```python
# routes.py
from routing.router import Route
IndexRoute = Route.create(path="/", form="Pages.Index")
AboutRoute = Route.create(path="/about", form="Pages.About")
ContactRoute = Route.create(path="/contact", form="Pages.Contact")
```
```
--------------------------------
### Configure Navigation Links with Path
Source: https://github.com/anvil-works/routing/blob/master/docs/migrating/index.md
Replace regular HTML links with `NavLink` components. Set the `path` attribute directly on the `NavLink` instance to define its navigation target.
```python
from ._anvil_designer import MainTemplate
from routing import router
class Main(MainTemplate):
def __init__(self, **properties):
self.nav_home.path = '/home'
self.nav_settings.path = '/settings'
self.init_components(**properties)
```
--------------------------------
### Open a form using the router
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Opens a form, potentially replacing `anvil.open_form` for client-side routing scenarios. This method is suitable when not using form parameters.
```python
open_form(form, **form_properties)
```
--------------------------------
### Create Feature Branch
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Create a new branch for your contributions. Use prefixes like 'fix/', 'feat/', or 'docs/' to categorize your branch.
```bash
git checkout -b fix/
```
```bash
git checkout -b feat/
```
```bash
git checkout -b docs/
```
--------------------------------
### Navigate with Form Properties
Source: https://github.com/anvil-works/routing/blob/master/docs/data-loading/index.md
When navigating to a route, use `form_properties` to pass data. This ensures that data is available without requiring an additional server call if the data is already known, such as when clicking a link within the app.
```python
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}
)
```
--------------------------------
### Navigate through history stack
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Use `go(n)` to navigate to the nth page in the browser's history stack. `go(0)` refers to the current page.
```python
go(n=0)
```
--------------------------------
### Navigating with the `navigate` function
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
The `navigate` function allows for programmatic navigation to a specific path, often triggered by user interactions like button clicks. It can only be called from client code.
```APIDOC
## POST /navigate
### Description
Navigates to a specified path within the application.
### Method
POST
### Endpoint
/navigate
### Parameters
#### Query Parameters
- **path** (string) - Optional - The path to navigate to. Can be relative. If not set, the current path is used.
- **params** (object) - Optional - Parameters for the path, e.g., `{"id": 123}`.
- **query** (object or function) - Optional - Query parameters to append to the URL. Can be a function that takes previous query parameters and returns new ones.
- **hash** (string) - Optional - The hash fragment to navigate to.
- **replace** (boolean) - Optional - If `True`, replaces the current URL in history (default is `False`).
- **nav_context** (object) - Optional - Contextual information passed to the next route.
- **form_properties** (object) - Optional - Properties to pass to the form when it is opened.
### Request Example
```json
{
"path": "/articles/:id",
"params": {"id": 123},
"query": {"tab": "income"},
"replace": false,
"form_properties": {"item": {"id": 123, "title": "Example Article"}}
}
```
### Response
#### Success Response (200)
- **status** (string) - Indicates successful navigation.
#### Response Example
```json
{
"status": "navigated"
}
```
```
--------------------------------
### Define Route with Load Data Method
Source: https://github.com/anvil-works/routing/blob/master/docs/data-loading/index.md
Implement the `load_data` method within a `Route` class to fetch data on the server before the form is initialized. This method receives `loader_args` including `nav_context` and `path_params`.
```python
# 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
```
```python
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)
```
--------------------------------
### Load and return route data
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Fetches and returns data for a specified URL or routing context. If no arguments are provided, it uses the current navigation context. Useful for sharing data across routes or for layouts needing route data.
```python
use_data(context_or_path_or_url=None, *, path=None, params=None, query=None, hash=None)
```
--------------------------------
### Accessing RoutingContext in Forms
Source: https://context7.com/anvil-works/routing/llms.txt
Demonstrates how to access route parameters, query strings, hash, loaded data, and navigation context within a form using RoutingContext. Also shows how to add event handlers for query changes and data loading.
```python
from routing.router import RoutingContext
class ArticleForm(ArticleFormTemplate):
def __init__(self, routing_context: RoutingContext, **properties):
self.routing_context = routing_context
# Access path parameters
article_id = routing_context.params["id"] # e.g., {"id": 123}
# Access query parameters
tab = routing_context.query.get("tab", "details") # e.g., {"tab": "comments"}
# Access URL hash
section = routing_context.hash # e.g., "#section-2"
# Access loaded data (from load_data method)
article = routing_context.data
# Access navigation context (passed via nav_context argument)
prev_page = routing_context.nav_context.get("prev_page")
# Access form_properties passed during navigation
if properties.get("item") is None:
properties["item"] = anvil.server.call("get_article", article_id)
self.init_components(**properties)
# Set up event handlers then raise initial events
routing_context.add_event_handler("query_changed", self.on_query_changed)
routing_context.add_event_handler("data_loaded", self.on_data_loaded)
routing_context.raise_init_events()
def on_query_changed(self, query, **event_args):
# React to query parameter changes
self.update_tab(query.get("tab", "details"))
def on_data_loaded(self, data, **event_args):
# React to data loading completion
self.refresh_data_bindings()
```
--------------------------------
### Register Links with Automatic Lifecycle Management
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/navigation-components.md
Use this method to automatically manage the lifecycle of registered links, ensuring cleanup on component removal. It's recommended for integrating client-side routing with existing HTML elements.
```python
from routing import router
class MainLayout(MainLayoutTemplate):
def __init__(self, **properties):
self.init_components(**properties)
# Register links tied to component lifecycle
router.register_links(
self.dom_nodes["header"],
active_class="active",
component=self # Auto setup on page added, cleanup on page removed
)
```
--------------------------------
### Route Methods
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/index.md
Details on the methods that can be overridden to customize route behavior.
```APIDOC
## Route Methods
### Description
Methods that can be overridden to customize route behavior, data loading, and parameter parsing.
### Methods
- `before_load` - Called before the route is matched. If it raises a `Redirect`, navigation is redirected. If it returns a dictionary, its contents are merged into the navigation context (`nav_context`).
Note - you may prefer not to override this method, and instead use the `@hooks.before_load` decorator.
See the navigation documentation for practical usage examples.
- `parse_query` - Should return a dictionary of query parameters. By default this returns the original query parameters.
- `parse_params` - Should return a dictionary of path parameters. By default this returns the original path parameters.
- `meta` - Should return a dictionary with the `title` and `description` of the page. This will be used to update the meta tags and the title of the page. By default this returns the original title and description.
- `load_data` - Called when the route is matched. The return value will be available in the `data` property of the `RoutingContext` instance. By default this returns `None`.
- `load_form` - This method is called with two arguments. The first argument is a form name (e.g. `"Pages.Index"`) or, if you are using cached forms, the cached form instance. The second argument is the `RoutingContext` instance. By default this calls `anvil.open_form` on the form.
- `cache_deps` - Returns an object, by default the `query` dictionary. This method is part of the process of creating caching keys. When a route needs to cache a form or data, it does so by storing it in a global dictionary under a caching key. This key is composed of the route's path and the return of its `cache_deps` method at the moment of caching. If, when accessing the same route, its `cache_deps` method returns something different than when caching first occurred, the caching key points to a different place within the cache, usually empty. The router thus understands this as a new route and navigates to it again.
```
--------------------------------
### Define a Default 404 Not Found Route
Source: https://context7.com/anvil-works/routing/llms.txt
Configure a default route to handle 404 Not Found errors by creating a `Route` subclass with `default_not_found = True`. This route will catch all unmatched URLs.
```python
from routing.router import Route, NotFound
# Default not found route (no path - catches all unmatched URLs)
class NotFoundRoute(Route):
form = "Pages.NotFound"
default_not_found = True
```
--------------------------------
### Registering Global Before-Load Hooks
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Hooks can be registered globally for all routes by assigning them to the base Route class. These global hooks will execute for every route.
```python
@hooks.before_load
def global_hook(self, nav_context, **loader_args):
nav_context["feature_enabled"] = True
Route.global_hook = global_hook
```
--------------------------------
### Migrate to TemplateWithContainerRoute
Source: https://context7.com/anvil-works/routing/llms.txt
Use `TemplateWithContainerRoute` for template-based navigation, maintaining compatibility with `anvil_extras.routing`. Define default templates and containers, and use `Redirect` for authentication flows.
```python
from routing.router import TemplateWithContainerRoute as BaseRoute, Redirect
# Set default template
BaseRoute.template = "Templates.Main"
BaseRoute.template_container = "content_panel"
class AuthMixin:
def before_load(self, **loader_args):
if not anvil.users.get_user():
raise Redirect(path="/login")
class HomeRoute(AuthMixin, BaseRoute):
path = "/"
form = "Pages.Home"
cache_form = True
class SettingsRoute(AuthMixin, BaseRoute):
path = "/settings"
form = "Pages.Settings"
template = "Templates.Settings" # Different template for this route
template_container_properties = {"full_width_row": True}
class LoginRoute(BaseRoute):
path = "/login"
form = "Pages.Login"
template = "Templates.Auth" # Minimal template for auth pages
```
--------------------------------
### Use NavigationBlocker Context Manager
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/blocking-navigation.md
The `NavigationBlocker` context manager provides a simpler way to block navigation. Setting `warn_before_unload=True` will trigger the browser's default confirmation dialog when navigating away. This is useful for temporary blocking, such as while an alert is displayed.
```python
from routing.router import NavigationBlocker
class Form(FormTemplate):
def __init__(self, **properties):
self.init_components(**properties)
def run_important_alert(self, **event_args):
with NavigationBlocker(warn_before_unload=True):
alert("Important Alert", dismissible=False)
```
--------------------------------
### Navigate Directly with Parameter Value
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/params.md
Navigate to a route by providing the full path including the parameter value. The router will automatically parse this into the correct parameter.
```python
from routing.router import navigate
navigate(path="/authors/123")
# the params will still become {"id": "123"}
```
--------------------------------
### Listen to Router Events
Source: https://context7.com/anvil-works/routing/llms.txt
Attach event handlers for 'navigate', 'pending', and 'idle' events to manage analytics, loading indicators, or custom navigation logic. Ensure handlers are removed when the form is hidden to prevent memory leaks.
```python
from routing import router
class MainLayout(MainLayoutTemplate):
def __init__(self, **properties):
self.init_components(**properties)
def form_show(self, **event_args):
router.add_event_handler("navigate", self.on_navigate)
router.add_event_handler("pending", self.on_pending)
router.add_event_handler("idle", self.on_idle)
def form_hide(self, **event_args):
router.remove_event_handler("navigate", self.on_navigate)
router.remove_event_handler("pending", self.on_pending)
router.remove_event_handler("idle", self.on_idle)
def on_navigate(self, **event_args):
# Track page views
context = router.get_routing_context()
analytics.track("page_view", {"path": context.path})
def on_pending(self, **event_args):
# Show global loading indicator
self.loading_indicator.visible = True
def on_idle(self, **event_args):
# Hide global loading indicator
self.loading_indicator.visible = False
```
--------------------------------
### Register HTML Links for Client-Side Routing
Source: https://context7.com/anvil-works/routing/llms.txt
Convert existing HTML links to use client-side routing with active state tracking using `router.register_links()`. This function can be automatically managed or manually cleaned up.
```python
from routing import router
class MainLayout(MainLayoutTemplate):
def __init__(self, **properties):
self.init_components(**properties)
# Automatic lifecycle management (recommended)
router.register_links(
self.dom_nodes["nav"],
active_class="active",
component=self # Auto-setup on page_added, cleanup on page_removed
)
# Or with manual cleanup
def form_show(self, **event_args):
self._cleanup = router.register_links(
self.dom_nodes["header"],
self.dom_nodes["footer"],
selector="a.nav-link", # Custom CSS selector
active_class="current-page",
)
def form_hide(self, **event_args):
if self._cleanup:
self._cleanup()
self._cleanup = None
# HTML template with data attributes for exact matching:
#
```
--------------------------------
### Registering Before-Load Hooks for Routes
Source: https://github.com/anvil-works/routing/blob/master/docs/api-reference.md
Use the @hooks.before_load decorator to register methods that execute before a route is loaded. Hooks receive and can update a nav_context dictionary. They can also return a dictionary to be merged into the context. Hooks are executed in reverse MRO order.
```python
from routing.router import Route, hooks, Redirect
class AuthenticatedRoute(Route):
# Style 1: Mutate nav_context directly
@hooks.before_load
def set_user(self, nav_context, **loader_args):
nav_context["user"] = get_current_user()
# Style 2: Return a partial dict to be merged into nav_context
@hooks.before_load
def set_user_partial(self, nav_context, **loader_args):
return {"user": get_current_user()}
@hooks.before_load
def check_permissions(self, nav_context, **loader_args):
user = nav_context.get("user")
if not user or not user.has_permission():
raise Redirect(path="/login")
```
--------------------------------
### Define Basic Meta Tags for a Route
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/meta.md
Override the `meta` method to return a dictionary of standard and Open Graph meta tags for a specific route. Ensure all relevant tags are explicitly defined for consistency.
```python
from routing import Route
class ProductRoute(Route):
path = "/product"
form = "Pages.Product"
def meta(self, **kwargs):
meta_data = {}
meta_data['title'] = "Product Page"
meta_data['description'] = "Details and specifications for our featured product."
meta_data['og:title'] = "Featured Product"
meta_data['og:description'] = "Learn more about our latest product release."
meta_data["og:image"] = "asset:product.jpeg" # Use a theme asset
return meta_data
```
--------------------------------
### Compose Hooks with Inheritance
Source: https://github.com/anvil-works/routing/blob/master/docs/navigating/index.md
Compose multiple hooks for a single route using the `@hooks.before_load` decorator, supporting mixins and inheritance. Hooks run in reverse MRO order.
```python
from routing.router import Route, hooks, Redirect
class AuthenticatedRoute(Route):
@hooks.before_load
def check_auth(self, **loader_args):
if not user_is_authenticated():
raise Redirect(path="/login")
return {"user": get_current_user()}
class FeatureFlagMixin:
@hooks.before_load
def add_feature_flag(self, **loader_args):
return {"feature_enabled": True}
class DashboardRoute(FeatureFlagMixin, AuthenticatedRoute):
path = "/dashboard"
form = "Pages.Dashboard"
@hooks.before_load
def dashboard_flag(self, **loader_args):
return {"show_dashboard": True}
```
--------------------------------
### Registering Custom NavLink and Anchor Components
Source: https://github.com/anvil-works/routing/blob/master/docs/theme/index.md
This Python code snippet demonstrates how to register custom `NavLink` and `Anchor` components within an Anvil preload module. Ensure your custom components (`MyNavLink`, `MyAnchor`) are correctly imported and that 'MY_PACKAGE_NAME' is replaced with your actual package name.
```python
import anvil
from ...MyNavLink import MyNavLink
from ...MyAnchor import MyAnchor
anvil.pluggable_ui.provide(
"MY_PACKAGE_NAME", {"routing.NavLink": MyNavLink, "routing.Anchor": MyAnchor}
)
```
--------------------------------
### Setting Meta Tags for SEO
Source: https://context7.com/anvil-works/routing/llms.txt
Override the `meta` method in a `Route` to dynamically set page titles, descriptions, and Open Graph tags for SEO and social sharing.
```python
from routing.router import Route
class ArticleRoute(Route):
path = "/articles/:id"
form = "Pages.Article"
def meta(self, **loader_args):
article_id = loader_args["params"]["id"]
# You can access data loaded in load_data here if needed
return {
"title": f"Article {article_id} | My Blog",
"description": f"Read article {article_id} on My Blog",
"og:title": "Amazing Article",
"og:description": "Check out this great article",
"og:image": "asset:article-cover.png", # Resolved to full URL
"twitter:card": "summary_large_image",
}
```
--------------------------------
### Run Pre-commit Checks Manually
Source: https://github.com/anvil-works/routing/blob/master/CONTRIBUTING.md
Execute all pre-commit checks across all files in the repository. This is useful for verifying code quality before committing.
```bash
pre-commit run --all-files
```
--------------------------------
### Define Routes with Route Class in Anvil
Source: https://context7.com/anvil-works/routing/llms.txt
Define URL paths and their corresponding forms by subclassing `Route`. The router matches routes in definition order. Supports path parameters and caching.
```python
from routing.router import Route
class IndexRoute(Route):
path = "/"
form = "Pages.Index"
class AboutRoute(Route):
path = "/about"
form = "Pages.About"
class ArticleRoute(Route):
path = "/articles/:id"
form = "Pages.Article"
cache_form = True
def parse_params(self, params):
# Convert path params from strings to proper types
return {"id": int(params["id"])}
# Alternative shorthand using Route.create()
ContactRoute = Route.create(path="/contact", form="Pages.Contact")
```
--------------------------------
### Perform History Navigation
Source: https://context7.com/anvil-works/routing/llms.txt
Utilize router functions for navigating back, forward, or reloading the current page. Use `hard=True` for a full server reload, and `hard=False` for a cached reload. Navigate specific history steps using integer arguments.
```python
from routing import router
class NavigationBar(NavigationBarTemplate):
def back_click(self, **event_args):
router.back() # Go back one page
def forward_click(self, **event_args):
router.forward() # Go forward one page
def reload_click(self, **event_args):
router.reload(hard=False) # Soft reload (from cache)
def hard_reload_click(self, **event_args):
router.reload(hard=True) # Hard reload (from server)
def go_to_specific_click(self, **event_args):
router.go(-2) # Go back 2 pages in history
```
--------------------------------
### Mixin for Ensuring User Login
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/redirecting.md
Create a reusable mixin to enforce user login for routes. The `EnsureUserMixin` redirects non-logged-in users to '/login'. Note that `anvil.users.get_user()` makes a server call.
```python
from routing.router import Route, Redirect
import anvil.users
class EnsureUserMixin:
def before_load(self, **loader_args):
if not anvil.users.get_user():
# Note this will make a server call
# so you may want to cache the call to anvil.users.get_user()
raise Redirect(path="/login")
class DashboardRoute(EnsureUserMixin, Route):
path = "/dashboard"
form = "Pages.Dashboard"
```
--------------------------------
### Default Route Form Loading
Source: https://github.com/anvil-works/routing/blob/master/docs/routes/forms.md
The default `load_form` implementation uses `anvil.open_form` with routing context and form properties.
```python
class Route:
def load_form(self, form, routing_context):
return anvil.open_form(
form, routing_context=routing_context, **routing_context.form_properties
)
```