Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Learn how to navigate the router programmatically using the router load method and the HTML load attribute for creating in-page routes.
This section details how you can use the load method on the router instance or load attribute to navigate to other parts of your application.
To use the load
method, you have first to inject the router into your component. This can be done easily by using the IRouter
decorator on your component constructor method. The following code will add a property to your component, which we can reference.
The load
method can accept a simple string value allowing you to navigate to another component without needing to supply configuration options.
You could also use the string value method to pass parameter values and do something like this where our route expects a product ID, and we pass 12:
The router instance load
method allows you to specify different properties on a per-use basis. The most common one is the title
property, which allows you to modify the title as you navigate your route.
A list of available load options can be found below:
title
— Sets the title of the component being loaded
parameters
— Specify an object to be serialized to a query string and then set to the query string of the new URL.
fragment
— Specify the hash fragment for the new URL.
These option values can be specified as follows and when needed:
The router also allows you to decorate links and buttons in your application using a load
attribute, which works the same way as the router instance load
method.
If you have routes defined on a root level (inside of my-app.ts
) you will need to add a forward slash in front of any routes you attempt to load. The following would work in the case of an application using configured routes.
The load attribute can do more than accept a string value. You can also bind to the load attribute for more explicit routing. The following example is a bit redundant as specifying route:product
would be the same as specifying load="product"
, but if you're wanting more explicit routing, it conveys the intent better.
And where things start to get interesting is when you want to pass parameters to a route. We use the params
configuration property to specify parameters.
In the above example, we provide the route (id
) value (via route: profile
). But, then also provide an object of parameters (via params.bind: { name: 'rob' }
).
These parameter values correspond to any parameters configured in your route definition. In our case, our route looks like this:
Depending on the scenario, you will want to redirect users in your application. Unlike using the load
API on the router where we manually route (for example, after logging in) redirection allows us to redirect inside router hooks.
Please see the Routing Lifecycle section to learn how to implement redirection inside your components.
The <au-viewport>
element is where all of the routing magic happens, the outlet. It supports a few different custom attributes, allowing you to configure how the router renders your components. It also allows you to use multiple viewports to create different layout configurations with your routing.
The router allows you to add multiple viewports to your application and render components into each viewport element by their name. The <au-viewport>
element supports a name attribute, which you'll want to use if you have more than one.
In this example, we have the main viewport for our main content, and another viewport called sidebar
for our sidebar content which is dynamically rendered. When using viewports, think of them like iframes, independent containers that can maintain their own states.
Routes will load in the default viewport element if there are one or more viewports. However, routes can be told to load into a specific viewport.
By specifying the viewport
property on a route, we can tell it to load into a specific route.
How to implement router "guards" into your applications to protect routes from direct access.
You might know router hooks as guards in other routers. Their role is to determine how components are loaded. They're pieces of code that are run in between.
The lifecycle hooks sharing API can be used to define reusable hook logic. In principle, nothing new needs to be learned: their behavior is the same as described in Lifecycle Hooks, with the only difference being that the view model instance is added as the first parameter.
If you worked with Aurelia 1, you might know these by their previous name: router pipelines.
Shared lifecycle hook logic can be defined by implementing a router lifecycle hook on a class with the @lifecycleHooks()
decorator. This hook will be invoked for each component where this class is available as a dependency. This can be either via a global registration or via one or more component-local registrations, similar to how, e.g. custom elements and value converters are registered.
In the example above, we register NoopAuthHandler
globally, which means it will be invoked for each routed component and return true
each time, effectively changing nothing.
Please note that you are not recommended to use global lifecycle hooks when you can avoid them, as they are run for each component, the same as you would use inside.
Because lifecycle hooks are invoked for each component, it is considered best practice to ensure that you name your lifecycle hooks appropriately, especially if you're working in a team where developers might not be aware of hooks modifying global component lifecycle behaviors.
While lifecycle hooks are indeed their own thing independent of the components you are routing to, the functions are basically the same as you would use inside an ordinary component.
This is the contract for ordinary route lifecycle hooks for components:
And this is the contract for shared lifecycle hooks
The only difference is the addition of the first viewModel
parameter. This comes in handy when you need the component instance since the this
keyword won't give you access like in ordinary component methods.
When dealing with route hooks, you might only want to apply those to specific components. Imagine an authentication workflow where you would want to allow unauthenticated users to access your login or contact page.
To do this, we can specify our route hook as a dependency in the component via the static dependencies
property, which takes an array of one or more dependencies.
Whenever someone tries to route to the SettingsPage
component, they will trigger the authentication hook you created. This per-component approach allows you to target the needed components you want behind a route hook.
Shared lifecycle hooks run in parallel with (but are started before) component instance hooks, and multiple of the same kind can be applied per component. When multiple hooks are registered per component, they are invoked in the registration order.
It is also permitted to define more than one hook per shared hook class:
The router emits several events via the Event Aggregator, allowing you to listen to router events. In some situations, you might opt for a router hook, but in other cases, an event might be what you are after.
A good example of where using events might be more appropriate is showing and hiding loaders and other parts of your applications related to routing.
The events fired are:
au:router:router-start
au:router:router-stop
au:router:navigation-start
au:router:navigation-end
au:router:navigation-cancel
au:router:navigation-error
To listen to these events, you subscribe to them using the event aggregator like this:
As you might expect, these events are named in an intuitive way depending on the action taking place inside the router.
You will want to listen to the end, cancel and error navigation events if you're relying on displaying and hiding parts of the UI based on the router to ensure you're checking for a true "done" state.
Aurelia comes with a powerful fully-featured router without the need to install any additional dependencies. If you are new to Aurelia, we recommend visiting the Getting Started section first to familiarise you with the framework.
You are here because you want to familiarize yourself with the router, so we'll make this quick. At the end of this tutorial, you will be familiar with all the concepts and APIs you need to add routing into your Aurelia applications.
While building our recipe application, we will cover the following:
How to create and configure routes
Navigating with load
in your views as well as view-models
Styling active links
Programmatically navigating using router APIs
Working with route parameters
A working demo and code for the following tutorial can also be found here.
To do this tutorial, you'll need a working Aurelia application. We highly recommend following the Quick Start guide to scaffold an application. However, for this tutorial, we have a starter Aurelia 2 application ready to go that we recommend. It will allow you to follow along and live code.
As you will see, it's a boring application with no routing or anything exciting. All it says is Recipes (hardly a recipe application).
The viewport is where our loaded routes are dynamically loaded into. When we click on a route, the <au-viewport>
element will be populated with the requested component.
Open my-app.html
and add in the <au-viewport>
and some navigation links.
This won't do anything just yet because we haven't enabled routing or created any routes.
Let's go into main.ts
and configure Aurelia to use routing. Import the RouterConfiguration
object and pass it to Aurelia's register
method.
We also configure the router to use push-state routing instead of the hash-based router. This gives us cleaner URLs.
Now that we have a viewport and the router enabled let's create some routes. We will start by adding our routes to our root my-app.ts
component.
The important thing to note here is that we are not specifying a component to load for these routes. We will do that shortly, but the routing structure is what we are creating here.
The first route has an empty path
, which means it's a default route (the router will load this first if no other route is requested). The second route recipes
tells the router when the user visits /recipes
to load our recipes component. Lastly, the recipes/:recipeId
route has a route parameter that allows us to load specific recipes.
We now need to create three components for our routes: the homepage, recipes page, and recipe detail page.
Unlike other frameworks and libraries, Aurelia works on the premise of a view-and-view model. If you familiarize yourself with the Getting Started section, you would already be familiar with these concepts.
Let's create the homepage first:
Let's create the recipes page:
Let's create the recipe detail page:
Lastly, let's import our components in my-app.ts
for the routes to load them.
In a non-tutorial application, you would have your API and server providing this data. But, for our tutorial will use a public recipe API instead. We could use mock data, but it would be a lot of data for a recipe application.
The MealDB is a fantastic free meal API that can give us recipes. We will use the Aurelia Fetch Client to get this data, which wraps up the native Fetch API.
In recipes-page.ts
add the following to the component view model:
We've loaded the recipes. Now it's time to display them. Open recipes-page.html
and add in the following:
We use Aurelia's repeat.for
functionality to loop over the recipes. But take notice of the link with load
attribute. Aurelia Router sees this and knows this is a route. We are providing the recipe detail route with the ID of the recipe.
It's not pretty, but we now have a list of recipes.
Sometimes a user might attempt to visit a recipe or page that doesn't exist. You might want to redirect the user or display a 404 page in those situations.
Let's create another component and call it fourohfour-page
We then specify on our <au-viewport>
what our fallback is. We need to import this 404 component to use it.
When we created our routes in my-app.ts
you might recall we created a recipe detail route which had a recipeId
parameter. Now, we are going to modify our recipe-detail
component to read this value from the URL and load the content.
There is a little more to unpack here. We inject the router because we will programmatically redirect away if the user attempts to view a non-existent recipe ID. We use the canLoad
method because loading our recipe view relies on existing recipes. If the recipe can't be found using the API, we redirect to the recipes page programmatically using the router.load
method.
Inside recipe-detail.html
we'll render our recipe:
The API we use returns ingredients on a line-by-line basis, so we've omitted those from this example. Now, you should be able to run your application and click on recipes to view them. A headline, image and instructions should now be visible.
As an additional step, you could add those in yourself.
The router automatically adds a active
class to all route links when they are active. Because our routes all live in my-app
We will edit my-app.css
We don't even have to import it (Aurelia does that automatically for us).
The active
class is now bold when active. We also do some quick styling tweaks to the route links to remove the underline and make them black so we can see the bold stand out more.
You just built a recipe application. It doesn't allow you to create recipes, and it's not very pretty, but it works. To see the application in action, a working demo of the above code can be seen here (or below).
Learn how to work with the @aurelia/router package to implement routing in your Aurelia applications.
Routing with Aurelia feels like a natural part of the framework. It can easily be implemented into your applications in a way that feels familiar if you have worked with other frameworks and library routers.
This section is broken up into two parts—a quick introduction to the router and router configuration.
Currently, two routers ship with Aurelia: router lite and core router. This section refers to the core router package that lives in @aurelia/router
— please see the warning note below on a caveat some developers encounter when working with the router.
Before you go any further: Please ensure you are importing from the @aurelia/router
package. Sometimes import extensions will autocomplete your imports and import from the aurelia
package, which currently exports the lite router. Eventually, the aurelia
package will export the @aurelia/router
package, but it currently does not. We have noticed, in many instances, that using the incorrect router imports is why routing is not working.
See how you can configure and implement routing in your Aurelia applications in only a few minutes. Of course, you will want to expand upon this as you build your routes. Here we only learn the bare minimum to get started.
To use the router, we have to register it with Aurelia. We do this inside of main.ts
(or main.js
if you're working with Javascript) — the router is then enabled after it is registered. You might already have code like this if you chose the routing example when generating using the Makes scaffolding tool.
Once again, it bears repeating. Please make sure your router imports are being imported from @aurelia/router in your `main.ts` file, but also in other parts of your Aurelia application as well.
Take note of the path
property which is empty. This tells the router that the HomePage
component is our default route. If no route is supplied, it will load this as the default component. The component
property is the component that will be loaded (self-explanatory). And the title
property is the title for our route.
And the view model for our component is equally simple:
First, let's look at the HTML. If you use the makes
tool to scaffold your Aurelia application. This might be my-app.html
load
Notice how we use a standard hyperlink <a>
tags, but they have an load
attribute instead of href
? This attribute tells the router that these are routable links. The router will translate these load
values into routes (either path or route name). By default, the router does also allow you to use href
for routes (a setting that can be turned off below configuring useHref
).
au-viewport
This tells the router where to display your components. It can go anywhere inside your HTML. It can also have a name (handy for instances where there are multiple au-viewport
elements), and you can have more than one.
The router allows you to configure how it interprets and handles routing in your Aurelia applications. The customize
method on the RouterConfiguration
object can be used to set numerous router settings besides the useUrlFragmentHash
value.
The title can be set for the overall application. By default, the title uses the following value: ${componentTitles}${appTitleSeparator}Aurelia
the component title (taken from the route or component) and the separator, followed by Aurelia.
In most instances, using the above string title is what you will want. You will want the solution below if you need to set the title or transform the title programmatically.
Using the transformTitle
method from the router customization, the default title-building logic can be overwritten. This allows you to set the title programmatically, perform translation (using Aurelia i18n or other packages) and more.
Are you trying to set the title using the Aurelia i18n package? Visit the section on configuring translated router titles here.
If you do not provide any configuration value, the default is hash-based routing. This means a hash will be used in the URL. If your application requires SEO-friendly links instead of hash-based routing links, you will want to use pushState.
We are performing the configuration inside of the main.ts
file, which is the default file created when using the Makes
CLI tool.
By calling the customize
method, you can supply a configuration object containing the property useUrlFragmentHash
and supplying a boolean value. If you supply true
this will enable hash mode. The default is true
.
If you are working with pushState routing, you will need a base HREF value in the head of your document. The scaffolded application from the CLI includes this in the index.html
file, but if you're starting from scratch or building within an existing application, you need to be aware of this.
PushState requires server-side support. This configuration is different depending on your server setup. For example, if you are using Webpack DevServer, you'll want to set the devServer
historyApiFallback
option to true
. If you are using ASP.NET Core, you'll want to call routes.MapSpaFallbackRoute
in your startup code. See your preferred server technology's documentation for more information on how to allow 404s to be handled on the client with push state.
The useHref
configuration setting is something all developers working with routing in Aurelia need to be aware of. By default, the router will allow you to use both href
as well as load
for specifying routes.
Where this can get you into trouble are external links, mailto links and other types of links that do not route. A simple example looks like this:
By default, this seemingly innocent and common scenario will trigger the router and cause an error in the console.
You have two options when it comes to working with external links. You can specify the link as external using the external
attribute.
Or, you can set useHref
to false
and only ever use the load
attribute for routes.
If you are using the router to render components in your application, there might be situations where a component attempts to be rendered that do not exist. This can happen while using direct routing (not configured routing)
To add in fallback behavior, we can do this in two ways. The fallback
attribute on the <au-viewport>
element or in the router customize
method (code).
Let's create the missing-page
component (this is required, or the fallback behavior will not work). First, we'll create the view model for our missing-page
component.
For the fallback
component, an ID gets passed as a parameter which is the value from the URL. If you were to attempt to visit a non-existent route called "ROB," the missingComponent
value would be ROB.
Now, the HTML.
By using the fallback
property on the customize
method when we register the router, we can pass a component.
Sometimes the fallback
attribute can be the preferred approach to registering a fallback. Import your fallback component and pass the name to the fallback
attribute. The same result, but it doesn't require touching the router registration.
The swapStrategy
configuration value determines how contents are swapped in a viewport when transitioning. Sometimes, you might want to change this depending on the type of data you are working with or how your routes are loaded. A good example of configuring the swap order is when you're working with animations.
attach-next-detach-current
(default)
attach-detach-simultaneously
detach-current-attach-next
detach-attach-simultaneously
While the docs do a great job explaining the intricacies of the router, sometimes you just need a code snippet and a brief explanation to do something. You will find code snippets for basic things, from creating routes to working with router hooks.
A component that is loaded as part of a route definition. The IRouteableComponent
When working with the router, sometimes you want to access the currently active route. The router provides an array of activeComponents
which can be one or more components currently active. In most instances, this array will only contain one component. However, if you are working with multiple viewports, this array will contain all components from those viewports.
By leveraging the route.match
property, we can get the currently active route. This is where you can access its data, path, name and other route configuration properties.
To get all registered routes, you can use the getRoutes
method from the rootScope
property of the router.
A parameter is denoted by the prefixed colon :
followed by the parameter's name. In this example, our parameter is called productId
, which is required for the route to load.
You can have more than one parameter (as many as you like):
Routes support a custom data
property allowing you to decorate your routes. Some use cases might include marking a route as requiring a user to be authenticated or an icon.
Some routes might be loaded into specific viewports in applications with multiple viewports. You can use the viewport
property on routes to specify which route.
Inside components displayed by routes, the best place is to load data inside canLoad
or load
hooks. If your view depends on the data being loaded (like a product detail page), use canLoad
otherwise, use load
. The first argument is any parameters passed through the route.
Using the canLoad
lifecycle hook, we can redirect users. In the following example, we redirect a user to a /products
route. You would have this wrapped in a check to determine if the component loads or the user is redirected away.
Learn all there is to know about creating routes in Aurelia.
The router takes your routing instructions and matches the URL to determine what components to render. When the URL patch matches the configured route path, the component is loaded in the case of configured routes.
To register routes, you can either use the @route
decorator or the static routes property static routes
to register one or more routes in your application.
The routing syntax used in the Aurelia router is similar to that of other routers you might have worked with before. The syntax will be very familiar if you have worked with Express.js routing.
A route is an object containing a few required properties that tell the router what component to render, what URL it should match and other route-specific configuration options.
At a minimum, a route must contain path
and component
properties, or path
and redirectTo
properties. The component
and redirectTo
properties can be used in place of one another, allowing you to create routes that point to other routes.
The path
property on a route is where you'll spend the most time configuring your routes. The path tells the router what to match in the URL, what parameters there are and if they're required.
A path can be made up of either a static string with no additional values or an array of strings. An empty path value is interpreted as the default route, and only one should be specified.
Named required parameters that are prefixed with a colon. :productId
when used in a path, a named required parameter might look like this:
This named parameter is denoted by the colon prefix and is called productId
which we will be able to access within our routed component.
Named optional parameters. Like required parameters, they are prefixed with a colon but end with a question mark.
In the above example, we have an optional parameter called variation
. We know it's optional because of the question mark at the end. This means it would still be valid if you visited this route with supplying the variation parameter.
Using optional name parameters is convenient for routes where different things can happen depending on the presence of those optional parameters.
Wildcard parameters. Unlike required and optional parameters, wildcard parameters are not prefixed with a colon, instead using an asterisk. The asterisk works as a catch-all, capturing everything provided after it.
In the above code example, we can have an endless path after which it is supplied as a value to the canLoad
and load
methods.
Besides the basics of path
and component
a route can have additional configuration options.
id
— The unique ID for this route
redirectTo
— Allows you to specify whether this route redirects to another route. If the redirectTo
path starts with /
it is considered absolute, otherwise relative to the parent path.
caseSensitive
— Determines whether the path
should be case sensitive. By default, this is false
transitionPlan
— How to behave when this component is scheduled to be loaded again in the same viewport. Valid values for transitionPlan are:
replace
— completely removes the current component and creates a new one, behaving as if the component changed.
invoke-lifecycles
— calls canUnload
, canLoad
, unload
and load
(default if only the parameters have changed)
none
— does nothing (default if nothing has changed for the viewport)
title
— Specify a title for the route. This can be a string, or it can be a function that returns a string.
viewport
— The name of the viewport this component should be loaded into.
data
— Any custom data that should be accessible to matched components or hooks. This is where you can specify data such as roles and other permissions.
routes
— The child routes that can be navigated from this route.
By specifying the redirectTo
property on our route, we can create route aliases. These allow us to redirect to other routes. We redirect our default route to the products page in the following example.
When creating routes, it is important to note that the component
property can do more than accept inline import statements. You can also import the component and specify the component class as the component property if you prefer.
If you are working with the Aurelia application generated using npx makes aurelia
you would already have a my-app.ts
file to place your routes in. It's the main component of the scaffolded Aurelia application.
As you will learn towards the end of this section, inline import statements allow you to implement lazy-loaded routes (which might be needed as your application grows in size).
If you have a lot of routes, the static property might be preferable from a cleanliness perspective.
If you have more than a few routes, it might be best practice to write them in a separate file and then import them inside your application.
The syntax for routes stays the same using the decorator. Just how they have defined changes slightly.
As your application grows, child routes can become a valuable way to organize your routes and keep things manageable. Instead of defining all your routes top-level, you can create routes inside your child components to keep them contained.
An example of where child routes might be useful in creating a dashboard area for authenticated users.
We add a route in our top-level my-app.ts
component where we added routes in our previous examples. Now, we will create the dashboard-page component.
You will notice we create routes the same way we learned further above. However, we are defining these inside a component we use for our dashboard section. Notice how we use the au-viewport
element inside of the dashboard-page
component.
Lastly, let's create our default dashboard component for the landing page.
Now, we can contain all dashboard-specific routes inside of our dashboard-page
component for dashboard views. Furthermore, it allows us to implement route guards to prevent unauthorized users from visiting the dashboard.
When a user attempts to visit a route that does not exist, we want to catch this route attempt using a catch-all route. We can use a wildcard *
to create a route that does this.
When using a catch-all wildcard route, ensure that it is the last route in your routes array, so it does not hijack any other valid route first.
A good use of a catch-all route might be to redirect users away to a landing page. For example, if you had an online store, you might redirect users to a products page.
You can also specify a component that gets loaded like a normal route:
Most modern bundlers like Webpack support lazy bundling and loading of Javascript code. The Aurelia router allows you to create routes that are lazily loaded only when they are evaluated. What this allows us to do is keep the initial page load bundle size down, only loading code when it is needed.
By specifying an arrow function that returns an inline import
we are telling the bundler that our route is to be lazily loaded when requested.
Inline import statements are a relatively new feature. Inside your tsconfig.json file, ensure your module property is set to esnext to support inline import statements using this syntax.
We went over creating routes with support for parameters in the creating routes section, but there is an additional property you can specify on a route called data,
, which allows you to associate metadata with a route.
This data property will be available in the routable component and can be a great place to store data associated with a route, such as roles and auth permissions. In some instances, the route parameters can be used to pass data, but for other use cases, you should use the data property.
A common scenario is styling an active router link with styling to signify that the link is active, such as making the text bold. When a route is active, by default, a CSS class name of active
will be added to the route element.
In your HTML, if you were to create some links with load
attributes and visit one of those routes, the active
class would be applied to the link for styling. In the following example, visiting the about route would put class="active"
onto our a
element.
The routing lifecycle allows you to run code at different points of the routing lifecycle such as fetching data or changing the UI.
Inside your routable components which implement the IRouteableComponent
interface, certain methods are called at different points of the routing lifecycle. These lifecycle hooks allow you to run code inside of your components, such as fetching data or changing 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 IRouteableComponent
will ensure that your code editor provides you with intellisense to make working with these lifecycle hooks easier.
The canLoad
method is called upon attempting to load the component. If your route has any parameters supplied, they will be provided to the canLoad
method as an object with one or more parameters as the first argument.
If you were loading data from an API based on values provided in the URL, you would most likely do that inside canLoad
if the view is dependent on the data successfully loading.
The canLoad
method allows you to determine if the component should be loaded or not. If your component relies on data being present from the API or other requirements being fulfilled before being allowed to render, this is the method you would use.
When working with the canLoad
method, you can use promises to delay loading the view until a promise and/or promises have been resolved. The component would be loaded if we were to return true
from this method.
If you wanted to load data from an API, you could make the canLoad
method async, which would make it a promise-based method. You would be awaiting an actual API call of some kind in place of ....load data
Unlike other async methods, if the promise does not resolve, the component will not load. The canLoad
lifecycle method tells the router if the component is allowed to load. It's a great router method for components that rely on data loading such as product detail or user profile pages.
Not only can we allow or disallow the component to be loaded, but we can also redirect it. If you want to redirect to the root route, return a string with an /
inside it. You can return a route ID, route path match or navigation instruction from inside this callback to redirect.
Returning a boolean false, string or RoutingInstruction from within the canLoad
function will cancel the router navigation.
The loading
method is called when your component is navigated to. If your route has any parameters supplied, they will be provided to the load
method as an object with one or more parameters as the first argument.
If you are loading data from an API based on values provided in the URL and the rendering of this view is not dependent on the data being successfully returned, you can do that inside of load
.
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.
All of the above code examples for canLoad
can be used with loading
and will work the same, with exception of being able to return true
or false
boolean values to prevent the component being loaded (as we just mentioned).
The canUnload
method is called when a user attempts to leave a routed view. The first argument of this callback is a INavigatorInstruction
it provides information about the next route. You can return a component, boolean or string value from this method.
Like the canLoad
method, this is just the inverse. It determines if we can navigate away from the current component.
The unloading
method is called if the user is allowed to leave and is in the process of leaving. The first argument of this callback is a INavigatorInstruction
it provides information about the next route.
Like the loading
method, this is just the inverse. It is called when the component is unloaded (provided canUnload
wasn't false).
A common router scenario is you want to route to a specific component, say a component that displays product information based on the ID in the URL. You request the API to get the information and display it.
Two asynchronous lifecycles are perfect for dealing with loading data: canLoad
and load
- both supporting returning a promise (or async/await).
If the component you are loading absolutely requires the data to exist on the server and be returned, the canLoad
lifecycle method is the best place to do it. Using our example of a product page, if you couldn't load product information, the page would be useful, right?
From the inside canLoad
you can redirect the user elsewhere or return false to throw an error.
Similarly, if you still want the view to load, even if we can't get the data, you would use the loading
lifecycle callback.
When you use load
and async
the component will wait for the data to load before rendering.
If you worked with routing in Aurelia 1, you might be accustomed to a currentInstruction
property available on the router. In Aurelia 2, this property does not exist. There are, however, two properties on the router called activeNavigation
and activeComponents
which can be used to achieve a similar result. You can also reference the instruction itself from within route lifecycle functions.
The activeNavigation
property contains quite a few properties but most notably has the current URL path, query parameters and other navigation-specific values. You might want to get information about the current route.
We can get information about the current route by accessing the activeComponents
array and determining the active component. Still, it is possible that more than one component will be in this array. An easier way is to get the route instruction on the canLoad
and loading
lifecycle methods.
It might seem like a mouthful, but to get the current instruction that resulted in the viewport's current content, this is the current approach to take from within those aforementioned methods inside your components.
The parameters
object contains a Javascript object of any URL parameters. If your URL contains /?myprop=22&frag=0
then this object would contain {myprop: '22', frag: '0'}
, allowing you to get fragment values.
While you would often set the title of a route in your route configuration object using the title
property, sometimes you want the ability to specify the title property from within the routed component itself.
You can achieve this from within the canLoad
and load
methods in your component. By setting the next.title
property, you can override or transform the title.
Strategies for stateful router animation
A common scenario in a single-page application is page transitions. When a page loads or unloads, an animation or transition effect might be used to make it feel more interactive and app-like.
At first glance, this might look like a lot of code, but we do the animation inside of the attaching
and detaching
hooks. Using the Anime.js animation library, we create two animation functions for sliding our views in and out.
We use the created
lifecycle callback to access the host element (the outer element of our custom element) which we will animate. Most of the other callbacks determine the direction we are heading in.
We inject out AnimationHooks
class into our main component, but we also inject it into the sub-components we want to animate. We avoid setting our hook globally, or it would run for all components (which you might want).
As you can see, besides some router-specific lifecycle methods, animating with the router isn't router-specific and leverages Aurelia lifecycles.
A link to a demo of slide in and out animations based on routing can be seen below:
If you are looking for details on configuring the router (set titles, handle unknown routes, etc.), please see the section at the end of this guide.
The following getting started guide assumes you have an Aurelia application already created. If not, to get Aurelia installed in minutes.
Now, we create our routes. We'll do this inside my-app.ts
and use the static routes
property. Please note that there is also a @routes
decorator, which is detailed inside the section.
For our two routes, we import and provide their respective components. Your components are just classes and can be very simple. Here is the HomePage
component. Please note that you can use inline imports when creating routes, also detailed in the section.
Can't find what you're looking for in this section? We have a section detailing many tasks for working with the router, from passing data between routes to route guards.
This section is not for catch-all/404 routes. If you are using configured routing, you are looking for the .
Still, confused or need an example? You can find an example application with routing over on GitHub .
As outlined in the section, routes can be specified using the routes
decorator or the static routes
property.
Parameters are supplied to canLoad
and loading
router lifecycle callbacks as the first argument. They are passed as an object with key/value pairs. Please consult the section to learn how to access them.
By leveraging , we can perform animations and transition effects in code.