### 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 ) ```