forked from aurelia/aurelia
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GitBook: [aurelia#130] Routing improvements
- Loading branch information
1 parent
f74f61c
commit 552f8db
Showing
6 changed files
with
133 additions
and
279 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,17 @@ | |
|
||
## Getting started | ||
|
||
Aurelia being a fully-featured framework comes with a router package ready to use in the framework. | ||
Aurelia being a fully-featured framework comes with a router package ready to use in the framework. To register the plugin in your application, you can pass in the router object to the `register` method inside of the file containing your Aurelia initialization code.  | ||
|
||
To register the plugin in your application, you can pass in the router object to the `register` method inside of the file containing your Aurelia initialization code.  | ||
{% hint style="success" %} | ||
Looking for a shortcut? If you generate a new Aurelia application using `npx makes aurelia` and choose routing, you can skip over this getting started section as we talk about code that is automatically added in for you as part of the scaffolding process. | ||
{% endhint %} | ||
|
||
We import the `RouterConfiguration` class from the `aurelia` package, which allows us to register our router and change configuration settings. | ||
|
||
This file is generated from the `npx makes aurelia` scaffolding tool and is found in `src/main.ts` | ||
|
||
{% code title="main.ts" %} | ||
```typescript | ||
import Aurelia, { RouterConfiguration } from 'aurelia'; | ||
|
||
|
@@ -18,9 +21,14 @@ Aurelia | |
.app(component) | ||
.start(); | ||
``` | ||
{% endcode %} | ||
|
||
The `RouterConfiguration` object is highly configurable and allows us to change how routing works in our Aurelia applications. It will use some default settings if you don't change anything (like we have done). By default, the router will assume you are using pushState routing. | ||
|
||
{% hint style="info" %} | ||
A scaffolded Aurelia application using `makes` and routing selected will automatically add in router configuration for you. The code above is taken from a newly generated Aurelia application and shown for reference. | ||
{% endhint %} | ||
|
||
### Create a viewport | ||
|
||
After registering the router plugin, we need to add a viewport element to the default root component. If you scaffolded your application using Makes, then your root component by default is `my-app.ts` and `my-app.html`. | ||
|
@@ -33,115 +41,10 @@ Inside of `my-app.html` you can add the following to get you started: | |
|
||
We will get into the specifics of the `<au-viewport>` element later on. Right now, all you need is this one simple element in the same view as the accompanying view model that contains the routes. | ||
|
||
To learn more about configuring the viewport, please see the router docs on configuring the viewport in the [viewports section](viewports.md). | ||
|
||
## Changing the router mode (hash and pushState routing) | ||
|
||
If you do not provide any configuration value, the default as we saw above is pushState routing. If you prefer the hash to be used, you can enable this like so: | ||
|
||
```typescript | ||
import Aurelia, { RouterConfiguration } from 'aurelia'; | ||
|
||
Aurelia | ||
.register(RouterConfiguration.customize({ useUrlFragmentHash: true })) | ||
.app(component) | ||
.start(); | ||
``` | ||
|
||
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 `false`. | ||
|
||
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. | ||
|
||
```html | ||
<head> | ||
<base href="/"> | ||
</head> | ||
``` | ||
|
||
{% hint style="warning" %} | ||
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. | ||
{% endhint %} | ||
|
||
## Router Configuration options | ||
|
||
The `customize` method on the `RouterConfiguration` object can be used to set numerous router settings besides the `useUrlFragmentHash` value. | ||
|
||
{% hint style="info" %} | ||
While router hooks can be configured on a global basis from within `customize` those are covered in their own dedicated section. | ||
Like router configuration, using `makes` will automatically add in a `au-viewport` element to `my-app.html` if you choose routing as part of the scaffolding decision process. | ||
{% endhint %} | ||
|
||
### Styling Active Router Links | ||
|
||
A common scenario is styling an active router link with styling to signify that the link is active, such as making the text bold. The `load` attribute has a bindable property named `active` which you can bind to a property on your view-model to use for conditionally applying a class: | ||
|
||
```css | ||
.active { | ||
font-weight: bold; | ||
} | ||
``` | ||
|
||
```html | ||
<a active.class="_settings" load="route:settings; active.bind:_settings"> | ||
Settings | ||
</a> | ||
``` | ||
|
||
The `active` property is a boolean value that we bind to. In this example, we use an underscore to signify this is a private property. | ||
|
||
{% hint style="info" %} | ||
You do not need to explicitly declare this property in your view model since it is a from-view binding. The underscore prefix of \_settings has no special meaning to the framework, it is just a common convention for private properties which can make sense for properties that are not explicitly declared in the view model. | ||
{% hint style="success" %} | ||
To learn more about configuring the viewport, please see the router docs on configuring the viewport in the [viewports section](viewports.md) | ||
{% endhint %} | ||
|
||
### Configuring route markup parsing using useHref | ||
|
||
The `useHref` configuration setting is something that 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 is external links, mailto links and other types of links that do not route. A simple example looks like this: | ||
|
||
```html | ||
<a href="mailto:[email protected]">Email Me</a> | ||
``` | ||
|
||
This seemingly innocent and common scenario by default will trigger the router and will 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. | ||
|
||
```html | ||
<a href="mailto:[email protected]" external>Email Me</a> | ||
``` | ||
|
||
Or, you can set `useHref` to `false` and only ever use the `load` attribute for routes. | ||
|
||
```typescript | ||
import Aurelia, { RouterConfiguration } from 'aurelia'; | ||
|
||
Aurelia | ||
.register(RouterConfiguration.customize({ | ||
useHref: false | ||
})) | ||
.app(component) | ||
.start(); | ||
``` | ||
|
||
### Configuring the same URL strategy | ||
|
||
In some scenarios, the same route may be used more than once. On a global level, you can configure the same URL strategy to either be `replace` or `ignore`. The default value for this is `ignore`. | ||
|
||
```typescript | ||
import Aurelia, { RouterConfiguration } from 'aurelia'; | ||
|
||
Aurelia | ||
.register(RouterConfiguration.customize({ | ||
sameUrlStrategy: 'replace' | ||
})) | ||
.app(component) | ||
.start(); | ||
``` | ||
|
||
### Configuring the route swap order | ||
|
||
The `swapStrategy` configuration value determines how contents are swapped in a viewport when transitioning. The default value for this setting is `sequential-`remove`-first` — however, in some instances, you might want to change this depending on the type of data you are working with or how your routes are loaded. | ||
|
||
* `sequential-add-first` | ||
* `sequential-remove-first` | ||
* `parallel-remove-first` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
175 changes: 8 additions & 167 deletions
175
docs/user-docs/developer-guides/routing/direct-routing.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,176 +1,17 @@ | ||
--- | ||
description: >- | ||
How to leverage direct routing in your Aurelia applications using the | ||
convention-based direct router feature. | ||
Learn about an alternative routing option available in the Aurelia router that | ||
offers zero-configuration routing. | ||
--- | ||
|
||
# Direct routing | ||
# Direct Routing | ||
|
||
{% hint style="info" %} | ||
`Please note that we currently have an interim router implementation and that some (minor) changes to application code might be required when the original router is added back in.` | ||
{% endhint %} | ||
The Aurelia router supports different ways of routing. In sections such as Creating Routes, we talk about configured routes, which require you to map out your routes ahead of time and have an understanding of how things are mapped. With direct routing, the router works on the basis of zero-configuration routing. | ||
|
||
Aurelia is known for its conventions-based approach to building applications. It provides you with a set of sane defaults and ways of doing certain things in your app, which help save time and make your life easier. The router in Aurelia 2 is no exception. | ||
It might sound like some kind of developer-hype, but zero-configuration routing in the form of direct routing allows you to route in your application without configuring your routes. Think of the direct router as a form of dynamic composition for routing. | ||
|
||
{% hint style="success" %} | ||
**What you will learn in this section** | ||
By default, you don't need to tell the router you want to use direct routing. Instead of referencing route names or paths, you reference component names. | ||
|
||
* What direct routing is | ||
* How to create parameter-less routes | ||
* How to create routes with parameters | ||
* How to pass data through routes | ||
* How to name parameters | ||
{% endhint %} | ||
|
||
## What Is Direct Routing? | ||
|
||
To put it in simple terms, direct routing is routing without route configuration. Unlike other routers you might be familiar with, you do not need to specify your routes upfront in code. The direct routing functionality works for all kinds of routing from standard routes to routes with parameters, child routing and more. | ||
|
||
### How it works | ||
|
||
You start off by registering the plugin in your app, you add in an `<au-viewport>` element where your routes will be displayed. Then using the `load` attribute on your links, you can tell the router to render a specific component. | ||
|
||
Components which have been globally registered inside the `register` method, or imported inside of the view can be rendered through the router. | ||
|
||
## Direct routing example | ||
|
||
As you will see, the direct routing approach requires no configuration. We import our component and then reference it by name inside of the `load` attribute. | ||
|
||
{% tabs %} | ||
{% tab title="my-app.html" %} | ||
```markup | ||
<import from="./test-component.html"></import> | ||
<ul> | ||
<li><a load="test-component">Test Component</a></li> | ||
</ul> | ||
<au-viewport></au-viewport> | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.html" %} | ||
```markup | ||
<h1>Hello world, I'm a test component.</h1> | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
The `load` attribute on our link denotes that this link is to navigate to a component using the router. Inside of the `load` attribute, we pass in the name of the component \(without any file extension\). As you can see, HTML only components are supported by the router. | ||
|
||
## Routes with parameters | ||
|
||
The simple example above shows you how to render a component using the router, and now we are going to introduce support for parameters. A parameter is a dynamic value in your route which can be accessed inside of the routed component. For example, this might be a product ID or a category name. | ||
|
||
To access parameters from the route, we can get those from the router lifecycle hook called `load` which also supports promises and can be asynchronous. | ||
|
||
{% tabs %} | ||
{% tab title="my-app.html" %} | ||
```markup | ||
<import from="./test-component"></import> | ||
<ul> | ||
<li><a load="test-component(hello)">Test Component</a></li> | ||
</ul> | ||
<au-viewport></au-viewport> | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.ts" %} | ||
```typescript | ||
import { IRouteViewModel } from 'aurelia'; | ||
|
||
export class TestComponent implements IRouteViewModel { | ||
load(parameters) { | ||
console.log(parameters); // Should display {0: "hello"} in the browser developer tools console | ||
} | ||
} | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.html" %} | ||
```markup | ||
<h1>Hello world, I'm a test component.</h1> | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
In this example, we are not telling the router the name of our parameters. By default, the router will pass an object keyed by index \(starting at 0\) for unnamed parameters. To access the value being given to our test component, we would reference it using `parameters['0']` which would contain `1` as the value. | ||
|
||
### **Inline named route parameters** | ||
|
||
You can name your route parameters inline by specifying the name inside of the `load` attribute on the component. | ||
|
||
{% tabs %} | ||
{% tab title="my-app.html" %} | ||
```markup | ||
<import from="./test-component"></import> | ||
<ul> | ||
<li><a load="test-component(named=hello)">Test Component</a></li> | ||
</ul> | ||
<au-viewport></au-viewport> | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.ts" %} | ||
```typescript | ||
import { IRouteViewModel } from 'aurelia'; | ||
|
||
export class TestComponent implements IRouteViewModel { | ||
load(parameters) { | ||
console.log(parameters); // Should display {named: "hello"} in the browser developer tools console | ||
} | ||
} | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.html" %} | ||
```markup | ||
<h1>Hello world, I'm a test component.</h1> | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
|
||
### **Named route parameters** | ||
|
||
It is recommended that unless you do not know the names of the parameters, that you supply the names inside of your routed component using the static class property `parameters` which accepts an array of strings corresponding to parameters in your route. | ||
|
||
While you can name them inline, specifying them inside of your component makes it easier for other people working in your codebase to determine how the component work. | ||
|
||
{% tabs %} | ||
{% tab title="my-app.html" %} | ||
```markup | ||
<import from="./test-component"></import> | ||
<ul> | ||
<li><a load="test-component(hello)">Test Component</a></li> | ||
</ul> | ||
<au-viewport></au-viewport> | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.ts" %} | ||
```typescript | ||
import { IRouteViewModel } from 'aurelia'; | ||
|
||
export class TestComponent implements IRouteViewModel { | ||
static parameters = ['id']; | ||
|
||
load(parameters) { | ||
console.log(parameters); // Should display {id: "hello"} in the browser developer tools console | ||
} | ||
} | ||
``` | ||
{% endtab %} | ||
|
||
{% tab title="test-component.html" %} | ||
```markup | ||
<h1>Hello world, I'm a test component.</h1> | ||
``` | ||
{% endtab %} | ||
{% endtabs %} | ||
## Direct Routing explained in code | ||
|
||
A picture is worth a thousand words, so let's go through the makeup of a direct router-based Aurelia application. Fortunately, for us, by choosing "direct routing" when prompted via `npx makes aurelia` we will have an application that uses direct routing by default. |
Oops, something went wrong.