LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • canLoad
  • Allow or disallowed loading components
  • Redirect to another view from canLoad
  • Accessing fragment and query
  • loading
  • canUnload
  • unloading
  • Order of invocations
  • Order of invocations of component lifecycle hooks
  • Inspecting current route and query inside lifecycle hooks

Was this helpful?

Export as PDF
  1. Getting to know Aurelia
  2. Routing
  3. @aurelia/router-lite

Lifecycle hooks

Learn about the different routing hooks and how to leverage those in terms of dis/allow loading or unloading as well as performing setup and teardown of a view.

Inside your routable components which implement the IRouteViewModel interface, there are certain methods that are called at different points of the routing lifecycle. These lifecycle hooks allow you to run code inside of your components such as fetch data or change the UI itself.

Router lifecycle hook methods are all completely optional. You only have to implement the methods you require. The router will only call a method if it has been specified inside of your routable component. All lifecycle hook methods also support returning a promise and can be asynchronous.

If you are working with components you are rendering, implementing IRouteViewModel will ensure that your code editor provides you with intellisense to make working with these lifecycle hooks in the appropriate way a lot easier.

import {
  IRouteViewModel,
  Params,
  RouteNode,
  NavigationInstruction,
} from '@aurelia/router-lite';

export class MyComponent implements IRouteViewModel {
  canLoad?(
    params: Params,
    next: RouteNode,
    current: RouteNode | null
  ): boolean
    | NavigationInstruction
    | NavigationInstruction[]
    | Promise<boolean | NavigationInstruction | NavigationInstruction[]>;
  loading?(params: Params, next: RouteNode, current: RouteNode | null): void | Promise<void>;
  canUnload?(next: RouteNode | null, current: RouteNode): boolean | Promise<boolean>;
  unloading?(next: RouteNode | null, current: RouteNode): void | Promise<void>;
}

Using the canLoad and canUnload hooks you can determine whether to allow or disallow navigation to and from a route respectively. The loading and unloading hooks are meant to be used for performing setup and clean up activities respectively for a view. Note that all of these hooks can return a promise, which will be awaited by the router-lite pipeline. These hooks are discussed in details in the following section.

canLoad

The canLoad method is called upon attempting to load the component. It allows you to determine if the component should be loaded or not. If your component relies on some precondition being fulfilled before being allowed to render, this is the method you would use.

To disallow loading the component you can return a boolean false. You can also return a navigation instruction to navigate the user to a different view. These are discussed in the following sections.

Allow or disallowed loading components

The following example shows that a parameterized route, such as /c1/:id?, can only be loaded if the value of id is an even number. Note that the value of the id parameter can be grabbed from the the first argument (params) to the canLoad method.

import { Params } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@customElement({
  name: 'c-one',
  template: `c1 \${id}`,
})
export class ChildOne {
  private id: number;
  public canLoad(params: Params): boolean {
    const id = Number(params.id);
    if (!Number.isInteger(id) || id % 2 != 0) return false;
    this.id = id;
    return true;
  }
}

You can also see this example in action below.

Redirect to another view from canLoad

import { NavigationInstruction, Params } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@customElement({
  name: 'c-one',
  template: `c1 \${id}`,
})
export class ChildOne {
  private id: number;
  public canLoad(params: Params): boolean | NavigationInstruction {
    const id = Number(params.id);
    // If the id is not an even number then redirect to c2
    if (!Number.isInteger(id) || id % 2 != 0) return `c2/${params.id}`;
    this.id = id;
    return true;
  }
}

You can also see this example in action below.

import { NavigationInstruction, Params } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@customElement({
  name: 'c-one',
  template: `c1 \${id}`,
})
export class ChildOne {
  private id: number;
  public canLoad(params: Params): boolean | NavigationInstruction {
    const id = Number(params.id);
    if (!Number.isInteger(id) || id % 2 != 0)
      return { component: 'r2', params: { id: params.id } };
    this.id = id;
    return true;
  }
}

Note that you can also choose to return a sibling navigation instructions. This can be done by returning an array of navigation instructions.

import { NavigationInstruction, Params } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';
import { Workaround } from './workaround';

@customElement({
  name: 'c-one',
  template: `c1 \${id}`,
})
export class ChildOne {
  private id: number;
  public canLoad(params: Params): boolean | NavigationInstruction {
    const id = Number(params.id);
    if (!Number.isInteger(id) || id % 2 != 0)
      return [
        { component: Workaround },
        { component: 'r2', params: { id: params.id } },
      ];
    this.id = id;
    return true;
  }
}

You can also see the example in action below.

Accessing fragment and query

Apart from accessing the route parameter, the query and the fragment associated with the URL can also be accessed inside the canLoad hook. To this end, you can use the second argument (next) to this method.

The following example shows that id query parameter is checked whether that is an even number or not. If that condition does not hold, then user is redirected to a different view with the query and fragment.

import { NavigationInstruction, Params, RouteNode } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@customElement({
  name: 'c-one',
  template: `c1 \${id} fragment: \${fragment}`,
})
export class ChildOne {
  private id: number;
  private fragment: string;
  public canLoad(
    params: Params,
    next: RouteNode
  ): boolean | NavigationInstruction {
    this.fragment = next.fragment;
    const query = next.queryParams;
    const rawId = query.get('id');
    const redirectPath = `c2?${next.queryParams.toString()}${
      next.fragment ? `#${next.fragment}` : ''
    }`;
    if (rawId === null) return redirectPath;
    const id = Number(rawId);
    if (!Number.isInteger(id) || id % 2 != 0) return redirectPath;
    this.id = id;
    return true;
  }
}

You can also see the example in action below.

loading

The loading method is called when your component is navigated to. If your route has any parameters supplied, they will be provided to the loading method as an object with one or more parameters as the first argument.

In many ways, the loading method is the same as canLoad with the exception that loading cannot prevent the component from loading. Where canLoad can be used to redirect users away from the component, the loading method cannot.

This lifecycle hook can be utilized to perform setup; for example, fetching data from backend API etc.

All of the above code examples for canLoad can be used with load and will work the same with the exception of being able to return true or false boolean values to prevent the component being loaded.

One of the examples is refactored using loading hook that is shown below.

Following is an additional example, that shows that you can use the next.title property to dynamically set the route title from the loading hook.

import { IRouteViewModel, Params, RouteNode } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@customElement({
  name: 'c-one',
  template: `c1 \${msg}`,
})
export class ChildOne implements IRouteViewModel {
  private msg: string;
  public loading(params: Params, next: RouteNode) {
    this.msg = `loaded with id: ${params.id}`;
    next.title = 'Child One';
  }
}

canUnload

The canUnload method is called when a user attempts to leave a routed view. The first argument (next) of this hook is a RouteNode which provides information about the next route.

This hook is like the canLoad method but inverse. You can return a boolean false from this method, to disallow the router-lite to navigate away from the current component.

The following example shows that before navigating away, the user is shown a confirmation prompt. If the user agrees to navigate way, then the navigation is performed. The navigation is cancelled, if the user does not confirm.

import { resolve } from 'aurelia';
import { IRouteViewModel, Params, RouteNode } from '@aurelia/router-lite';
import { IPlatform } from '@aurelia/runtime-html';

export class ChildOne implements IRouteViewModel {

  private readonly platform: IPlatform = resolve(IPlatform);

  public canUnload(next: RouteNode, current: RouteNode): boolean {
    const from = current.computeAbsolutePath();
    const to = next.computeAbsolutePath();
    return this.platform.window.confirm(
      `Do you want to navigate from '${from}' to '${to}'?`
    );
  }
}

You can see this example in action below.

unloading

The unloading hook is called when the user navigates away from the current component. The first argument (next) of this hook is a RouteNode which provides information about the next route.

This hook is like the loading method but inverse.

The following example shows that a unloading hook logs the event of unloading the component.

public unloading(next: RouteNode): void {
  this.logger.log(
    `unloading for the next route: ${next.computeAbsolutePath()}`
  );
}

This can also be seen in the live example below.

Order of invocations

For completeness it needs to be noted that the canLoad hook is invoked before loading and canUnload hook is invoked before unloading. In the context of swapping two views/components it is as follows.

  • canUnload hook (when present) of the current component is invoked.

  • canLoad hook (when present) of the next component (assuming that the canUnload returned true) is invoked.

  • unloading hook (when present) of the current component is invoked.

  • loading hook (when present) of the current component is invoked.

Note that the last 2 steps may run in parallel, if the hooks are asynchronous.

Order of invocations of component lifecycle hooks

<app-root>
  <component-one>
    <component-two>
    </component-two>
  </component-one>
</app-root>

In this case, the component lifecycle hooks are invoked in the following order.

  1. component-two attached.

  2. component-one attached.

  3. app-root attached.

This is also the same for the router-lite, except for the "application root" component. Tweaking the example above slightly, let us assume that we have the following constellation of components.

<app-root>
  <routed-view>
    <component-one>
      <component-two>
      </component-two>
    </component-one>
  </routed-view>
</app-root>

In this case, the component lifecycle hooks are invoked in the following order.

  1. app-root attached.

  2. component-two attached.

  3. component-one attached.

  4. routed-view attached.

Inspecting current route and query inside lifecycle hooks

Within lifecycle hooks like canLoad, loading, etc., you can also inspect the RouteNode:

import { IRouteViewModel, RouteNode, Params } from '@aurelia/router-lite';

export class Product implements IRouteViewModel {
  public loading(params: Params, next: RouteNode): void {
    const queryParam = next.queryParams.get('discount');
    const rawPath = next.computeAbsolutePath();
    console.log('Raw path is:', rawPath, 'and discount param is:', queryParam);
  }
}

If you prefer, you can also inject ICurrentRoute for a global view of the route, query, and title. Combine these approaches as you see fit for your canLoad, loading, etc.

PreviousNavigatingNextRouter hooks

Last updated 2 months ago

Was this helpful?

In case you are looking for the global/shared routing hooks, there is a separate dedicated for that.

Not only can we allow or disallow the component to be loaded, but we can also redirect. The simplest way is to return a string path from canLoad. In the following example, we re-write the , but instead of returning false, we return a path, where the user will be redirected.

If you prefer a more then you can also do so. Following is the same example using route-id and parameters object.

The component are invoked bottom-up. As an example, let us assume that we have the following constellation of components.

Note that the application root is attached before any other components are attached. This happens because the router-lite starts loading the first route only after the app-root, and thereby the viewport(s) it is hosting, are fully activated/attached. In order to load a route, the router needs registered viewports. The registration process of a viewport only happens during the attaching phase of a viewport. More details on this topic, can be found in this .

documentation section
structured navigation instructions
lifecycle hooks
GitHub issue
previous example
Logorouter-lite - canLoad - true/false - StackBlitzStackBlitz
Logorouter-lite - canLoad - string nav instruction - StackBlitzStackBlitz
Logorouter-lite - canLoad - routeid - nav instruction - StackBlitzStackBlitz
Logorouter-lite - canLoad - sibling nav instructions - StackBlitzStackBlitz
Logorouter-lite - canLoad - accessing fragment and query - StackBlitzStackBlitz
Logorouter-lite - loading - StackBlitzStackBlitz
Logorouter-lite - loading - title - StackBlitzStackBlitz
Logorouter-lite - canUnload - StackBlitzStackBlitz
Logorouter-lite - unloading - StackBlitzStackBlitz