In keeping with the [Separation of Concerns principle](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html "Separation of Concerns"), changes to the Crisis Center don't affect the `AppModule` or any other feature's component.
` for the crisis center child component.
### Child route configuration
As a host page for the "Crisis Center" feature, generate a `CrisisCenterHome` component in the `crisis-center` folder.
ng generate component crisis-center/crisis-center-home
Update the template with a welcome message to the `Crisis Center`.
Update the `crisis-center-routing.module.ts` you renamed after copying it from `heroes-routing.module.ts` file.
This time, you define child routes within the parent `crisis-center` route.
Notice that the parent `crisis-center` route has a `children` property with a single route containing the `CrisisListComponent`.
The `CrisisListComponent` route also has a `children` array with two routes.
These two routes navigate to the crisis center child components,
`CrisisCenterHomeComponent` and `CrisisDetailComponent`, respectively.
There are important differences in the way the router treats child routes.
The router displays the components of these routes in the `RouterOutlet` of the `CrisisCenterComponent`, not in the `RouterOutlet` of the `AppComponent` shell.
The `CrisisListComponent` contains the crisis list and a `RouterOutlet` to display the `Crisis Center Home` and `Crisis Detail` route components.
The `Crisis Detail` route is a child of the `Crisis List`.
The router [reuses components](#reuse) by default, so the `Crisis Detail` component is re-used as you select different crises.
In contrast, back in the `Hero Detail` route, [the component was recreated](#snapshot-the-no-observable-alternative) each time you selected a different hero from the list of heroes.
At the top level, paths that begin with `/` refer to the root of the application.
But child routes extend the path of the parent route.
With each step down the route tree, you add a slash followed by the route path, unless the path is empty.
Apply that logic to navigation within the crisis center for which the parent path is `/crisis-center`.
* To navigate to the `CrisisCenterHomeComponent`, the full URL is `/crisis-center` \(`/crisis-center` + `''` + `''`\)
* To navigate to the `CrisisDetailComponent` for a crisis with `id=2`, the full URL is `/crisis-center/2` \(`/crisis-center` + `''` + `'/2'`\)
The absolute URL for the latter example, including the `localhost` origin, is as follows:
localhost:4200/crisis-center/2
Here's the complete `crisis-center-routing.module.ts` file with its imports.
### Import crisis center module into the `AppModule` routes
As with the `HeroesModule`, you must add the `CrisisCenterModule` to the `imports` array of the `AppModule`
*before* the `AppRoutingModule`:
The import order of the modules is important because the order of the routes defined in the modules affects route matching.
If the `AppModule` were imported first, its wildcard route \(`path: '**'`\) would take precedence over the routes defined in `CrisisCenterModule`.
For more information, see the section on [route order](guide/router#route-order).
Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide the feature routes.
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
### Relative navigation
While building out the crisis center feature, you navigated to the
crisis detail route using an absolute path that begins with a slash.
The router matches such absolute paths to routes starting from the top of the route configuration.
You could continue to use absolute paths like this to navigate inside the Crisis Center feature, but that pins the links to the parent routing structure.
If you changed the parent `/crisis-center` path, you would have to change the link parameters array.
You can free the links from this dependency by defining paths that are relative to the current URL segment.
Navigation within the feature area remains intact even if you change the parent route path to the feature.
The router supports directory-like syntax in a *link parameters list* to help guide route name lookup:
| Directory-like syntax | Details |
|:--- |:--- |
| `./`
`no leading slash` | Relative to the current level. |
| `../` | Up one level in the route path. |
You can combine relative navigation syntax with an ancestor path.
If you must navigate to a sibling route, you could use the `../` convention to go up
one level, then over and down the sibling route path.
To navigate a relative path with the `Router.navigate` method, you must supply the `ActivatedRoute`
to give the router knowledge of where you are in the current route tree.
After the *link parameters array*, add an object with a `relativeTo` property set to the `ActivatedRoute`.
The router then calculates the target URL based on the active route's location.
Always specify the complete absolute path when calling router's `navigateByUrl()` method.
### Navigate to crisis list with a relative URL
You've already injected the `ActivatedRoute` that you need to compose the relative navigation path.
When using a `RouterLink` to navigate instead of the `Router` service, you'd use the same link parameters array, but you wouldn't provide the object with the `relativeTo` property.
The `ActivatedRoute` is implicit in a `RouterLink` directive.
Update the `gotoCrises()` method of the `CrisisDetailComponent` to navigate back to the Crisis Center list using relative path navigation.
Notice that the path goes up a level using the `../` syntax.
If the current crisis `id` is `3`, the resulting path back to the crisis list is `/crisis-center/;id=3;foo=foo`.
### Displaying multiple routes in named outlets
You decide to give users a way to contact the crisis center.
When a user clicks a "Contact" button, you want to display a message in a popup view.
The popup should stay open, even when switching between pages in the application, until the user closes it by sending the message or canceling.
Clearly you can't put the popup in the same outlet as the other pages.
Until now, you've defined a single outlet and you've nested child routes under that outlet to group routes together.
The router only supports one primary unnamed outlet per template.
A template can also have any number of named outlets.
Each named outlet has its own set of routes with their own components.
Multiple outlets can display different content, determined by different routes, all at the same time.
Add an outlet named "popup" in the `AppComponent`, directly following the unnamed outlet.
That's where a popup goes, once you learn how to route a popup component to it.
#### Secondary routes
Named outlets are the targets of *secondary routes*.
Secondary routes look like primary routes and you configure them the same way.
They differ in a few key respects.
* They are independent of each other
* They work in combination with other routes
* They are displayed in named outlets
Generate a new component to compose the message.
ng generate component compose-message
It displays a short form with a header, an input box for the message,
and two buttons, "Send" and "Cancel".
Here's the component, its template, and styles:
It looks similar to any other component in this guide, but there are two key differences.
**NOTE**:
The `send()` method simulates latency by waiting a second before "sending" the message and closing the popup.
The `closePopup()` method closes the popup view by navigating to the popup outlet with a `null` which the section on [clearing secondary routes](#clear-secondary-routes) covers.
#### Add a secondary route
Open the `AppRoutingModule` and add a new `compose` route to the `appRoutes`.
In addition to the `path` and `component` properties, there's a new property called `outlet`, which is set to `'popup'`.
This route now targets the popup outlet and the `ComposeMessageComponent` will display there.
To give users a way to open the popup, add a "Contact" link to the `AppComponent` template.
Although the `compose` route is configured to the "popup" outlet, that's not sufficient for connecting the route to a `RouterLink` directive.
You have to specify the named outlet in a *link parameters array* and bind it to the `RouterLink` with a property binding.
The *link parameters array* contains an object with a single `outlets` property whose value is another object keyed by one \(or more\) outlet names.
In this case there is only the "popup" outlet property and its value is another *link parameters array* that specifies the `compose` route.
In other words, when the user clicks this link, the router displays the component associated with the `compose` route in the `popup` outlet.
This `outlets` object within an outer object was unnecessary when there was only one route and one unnamed outlet.
The router assumed that your route specification targeted the unnamed primary outlet and created these objects for you.
Routing to a named outlet revealed a router feature:
you can target multiple outlets with multiple routes in the same `RouterLink` directive.
#### Secondary route navigation: merging routes during navigation
Navigate to the *Crisis Center* and click "Contact".
you should see something like the following URL in the browser address bar.
http://…/crisis-center(popup:compose)
The relevant part of the URL follows the `...`:
* The `crisis-center` is the primary navigation
* Parentheses surround the secondary route
* The secondary route consists of an outlet name \(`popup`\), a `colon` separator, and the secondary route path \(`compose`\)
Click the *Heroes* link and look at the URL again.
http://…/heroes(popup:compose)
The primary navigation part changed; the secondary route is the same.
The router is keeping track of two separate branches in a navigation tree and generating a representation of that tree in the URL.
You can add many more outlets and routes, at the top level and in nested levels, creating a navigation tree with many branches and the router will generate the URLs to go with it.
You can tell the router to navigate an entire tree at once by filling out the `outlets` object and then pass that object inside a *link parameters array* to the `router.navigate` method.
#### Clearing secondary routes
Like regular outlets, secondary outlets persists until you navigate away to a new component.
Each secondary outlet has its own navigation, independent of the navigation driving the primary outlet.
Changing a current route that displays in the primary outlet has no effect on the popup outlet.
That's why the popup stays visible as you navigate among the crises and heroes.
The `closePopup()` method again:
Clicking the "send" or "cancel" buttons clears the popup view.
The `closePopup()` function navigates imperatively with the `Router.navigate()` method, passing in a [link parameters array](#link-parameters-array).
Like the array bound to the *Contact* `RouterLink` in the `AppComponent`, this one includes an object with an `outlets` property.
The `outlets` property value is another object with outlet names for keys.
The only named outlet is `'popup'`.
This time, the value of `'popup'` is `null`.
That's not a route, but it is a legitimate value.
Setting the popup `RouterOutlet` to `null` clears the outlet and removes the secondary popup route from the current URL.
**Note:** All commands in the array passed to `Router.navigate()` target a _specific segment_ in the `UrlTree`.
We specify the parent of the `ActivatedRoute` as the `relativeTo` option because we want to remove `'popup'` from the segment which holds its reference.
It's important to always be aware of which segments the commands will be applied to.
When `relativeTo` is not provided to the `Router.navigate()` method, the commands are processed starting at the root.
We could omit the `relativeTo` option in this particular example because the `'popup'` outlet appears at the root level of the configuration.
If you want to close an outlet which appears at any segment depth, you could accomplish
this by creating a `UrlTree` from the current URL, recursively clearing segment `children` matching the outlet name, and finally
calling `Router.navigateByUrl()` with the `root` segment of the current `UrlTree`.
## Milestone 5: Route guards
At the moment, any user can navigate anywhere in the application any time, but sometimes you need to control access to different parts of your application for various reasons, some of which might include the following:
* Perhaps the user is not authorized to navigate to the target component
* Maybe the user must login \(authenticate\) first
* Maybe you should fetch some data before you display the target component
* You might want to save pending changes before leaving a component
* You might ask the user if it's okay to discard pending changes rather than save them
You add guards to the route configuration to handle these scenarios.
A guard's return value controls the router's behavior:
| Guard return value | Details |
|:--- |:--- |
| `true` | The navigation process continues |
| `false` | The navigation process stops and the user stays put |
| `UrlTree` | The current navigation cancels and a new navigation is initiated to the `UrlTree` returned |
**Note:** The guard can also tell the router to navigate elsewhere, effectively canceling the current navigation.
When doing so inside a guard, the guard should return `UrlTree`.
The guard might return its boolean answer synchronously.
But in many cases, the guard can't produce an answer synchronously.
The guard could ask the user a question, save changes to the server, or fetch fresh data.
These are all asynchronous operations.
Accordingly, a routing guard can return an `Observable` or a `Promise` and the router will wait for the observable or the promise to resolve to `true` or `false`.
**NOTE**:
The observable provided to the `Router` automatically completes after it retrieves the first value.
The router supports multiple guard methods:
| Guard interfaces | Details |
|:--- |:--- |
| [`canActivate`](api/router/CanActivateFn) | To mediate navigation *to* a route |
| [`canActivateChild`](api/router/CanActivateChildFn) | To mediate navigation *to* a child route |
| [`canDeactivate`](api/router/CanDeactivateFn) | To mediate navigation *away* from the current route |
| [`resolve`](api/router/ResolveFn) | To perform route data retrieval *before* route activation |
| [`canMatch`](api/router/CanMatchFn) | To control whether a `Route` should be used at all, even if the `path` matches the URL segment |
You can have multiple guards at every level of a routing hierarchy.
The router checks the `canDeactivate` guards first, from the deepest child route to the top.
Then it checks the `canActivate` and `canActivateChild` guards from the top down to the deepest child route.
If the feature module is loaded asynchronously, the `canMatch` guard is checked before the module is loaded.
With the exception of `canMatch`, if *any* guard returns false, pending guards that have not completed are canceled, and the entire navigation is canceled. If a `canMatch` guard returns `false`, the `Router` continues
processing the rest of the `Routes` to see if a different `Route` config matches the URL. You can think of this
as though the `Router` is pretending the `Route` with the `canMatch` guard did not exist.
There are several examples over the next few sections.
### `canActivate`: requiring authentication
Applications often restrict access to a feature area based on who the user is.
You could permit access only to authenticated users or to users with a specific role.
You might block or limit access until the user's account is activated.
The `canActivate` guard is the tool to manage these navigation business rules.
#### Add an admin feature module
This section guides you through extending the crisis center with some new administrative features.
Start by adding a new feature module named `AdminModule`.
Generate an `admin` folder with a feature module file and a routing configuration file.
ng generate module admin --routing
Next, generate the supporting components.
ng generate component admin/admin-dashboard
ng generate component admin/admin
ng generate component admin/manage-crises
ng generate component admin/manage-heroes
The admin feature file structure looks like this:
src/app/admin
admin
admin.component.css
admin.component.html
admin.component.ts
admin-dashboard
admin-dashboard.component.css
admin-dashboard.component.html
admin-dashboard.component.ts
manage-crises
manage-crises.component.css
manage-crises.component.html
manage-crises.component.ts
manage-heroes
manage-heroes.component.css
manage-heroes.component.html
manage-heroes.component.ts
admin.module.ts
admin-routing.module.ts
The admin feature module contains the `AdminComponent` used for routing within the feature module, a dashboard route and two unfinished components to manage crises and heroes.
Although the admin dashboard `RouterLink` only contains a relative slash without an additional URL segment, it is a match to any route within the admin feature area.
You only want the `Dashboard` link to be active when the user visits that route.
Adding an additional binding to the `Dashboard` routerLink,`[routerLinkActiveOptions]="{ exact: true }"`, marks the `./` link as active when the user navigates to the `/admin` URL and not when navigating to any of the child routes.
##### Component-less route: grouping routes without a component
The initial admin routing configuration:
The child route under the `AdminComponent` has a `path` and a `children` property but it's not using a `component`.
This defines a *component-less* route.
To group the `Crisis Center` management routes under the `admin` path a component is unnecessary.
Additionally, a *component-less* route makes it easier to [guard child routes](#can-activate-child-guard).
Next, import the `AdminModule` into `app.module.ts` and add it to the `imports` array to register the admin routes.
Add an "Admin" link to the `AppComponent` shell so that users can get to this feature.
#### Guard the admin feature
Currently, every route within the Crisis Center is open to everyone.
The new admin feature should be accessible only to authenticated users.
Write a `canActivate()` guard method to redirect anonymous users to the login page when they try to enter the admin area.
Create a new file named `auth.guard.ts` in the `auth` folder. The `auth.guard.ts` file will contain the `authGuard` function.
ng generate guard auth/auth
To demonstrate the fundamentals, this example only logs to the console, returns `true` immediately, and lets navigation proceed:
Next, open `admin-routing.module.ts`, import the `authGuard` function, and
update the admin route with a `canActivate` guard property that references it:
The admin feature is now protected by the guard, but the guard requires more customization to work fully.
#### Authenticate with `authGuard`
Make the `authGuard` mimic authentication.
The `authGuard` should call an application service that can log in a user and retain information about the current user.
Generate a new `AuthService` in the `auth` folder:
ng generate service auth/auth
Update the `AuthService` to log in the user:
Although it doesn't actually log in, it has an `isLoggedIn` flag to tell you whether the user is authenticated.
Its `login()` method simulates an API call to an external service by returning an observable that resolves successfully after a short pause.
The `redirectUrl` property stores the URL that the user wanted to access so you can navigate to it after authentication.
To keep things minimal, this example redirects unauthenticated users to `/admin`.
Revise the `authGuard` to call the `AuthService`.
This guard returns a synchronous boolean result or a `UrlTree`.
If the user is logged in, it returns `true` and the navigation continues.
Otherwise, it redirects to a login page; a page you haven't created yet.
Returning a `UrlTree` tells the `Router` to cancel the current navigation and schedule a new one to redirect the user.
#### Add the `LoginComponent`
You need a `LoginComponent` for the user to log in to the application.
After logging in, you'll redirect to the stored URL if available, or use the default URL.
There is nothing new about this component or the way you use it in the router configuration.
ng generate component auth/login
Register a `/login` route in the `auth/auth-routing.module.ts` file.
In `app.module.ts`, import and add `AuthModule` to the `AppModule` imports array.
### `canActivateChild`: guarding child routes
You can also protect child routes with the `canActivateChild` guard.
The `canActivateChild` guard is similar to the `canActivate` guard.
The key difference is that it runs before any child route is activated.
You protected the admin feature module from unauthorized access.
You should also protect child routes *within* the feature module.
Add the same `authGuard` to the `component-less` admin route to protect all other child routes at one time
instead of adding the `authGuard` to each route individually.
### `canDeactivate`: handling unsaved changes
Back in the "Heroes" workflow, the application accepts every change to a hero immediately without validation.
In the real world, you might have to accumulate the users changes, validate across fields, validate on the server, or hold changes in a pending state until the user confirms them as a group or cancels and reverts all changes.
When the user navigates away, you can let the user decide what to do with unsaved changes.
If the user cancels, you'll stay put and allow more changes.
If the user approves, the application can save.
You still might delay navigation until the save succeeds.
If you let the user move to the next screen immediately and saving were to fail \(perhaps the data is ruled invalid\), you would lose the context of the error.
You need to stop the navigation while you wait, asynchronously, for the server to return with its answer.
The `canDeactivate` guard helps you decide what to do with unsaved changes and how to proceed.
#### Cancel and save
Users update crisis information in the `CrisisDetailComponent`.
Unlike the `HeroDetailComponent`, the user changes do not update the crisis entity immediately.
Instead, the application updates the entity when the user presses the Save button and discards the changes when the user presses the Cancel button.
Both buttons navigate back to the crisis list after save or cancel.
In this scenario, the user could click the heroes link, cancel, push the browser back button, or navigate away without saving.
This example application asks the user to be explicit with a confirmation dialog box that waits asynchronously for the user's
response.
You could wait for the user's answer with synchronous, blocking code, however, the application is more responsive —and can do other work— by waiting for the user's answer asynchronously.
Generate a `Dialog` service to handle user confirmation.
ng generate service dialog
Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent.
The `window.confirm` is a blocking action that displays a modal dialog and waits for user interaction.
It returns an `Observable` that resolves when the user eventually decides what to do: either to discard changes and navigate away \(`true`\) or to preserve the pending changes and stay in the crisis editor \(`false`\).
Create a guard that checks for the presence of a `canDeactivate()` method in a component —any component.
ng generate guard can-deactivate
Paste the following code into your guard.
While the guard doesn't have to know which component has a `deactivate` method, it can detect that the `CrisisDetailComponent` component has the `canDeactivate()` method and call it.
The guard not knowing the details of any component's deactivation method makes the guard reusable.
Alternatively, you could make a component-specific `canDeactivate` guard for the `CrisisDetailComponent`.
The `canDeactivate()` method provides you with the current instance of the `component`, the current `ActivatedRoute`, and `RouterStateSnapshot` in case you needed to access some external information.
This would be useful if you only wanted to use this guard for this component and needed to get the component's properties or confirm whether the router should allow navigation away from it.
Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes.
Notice that the `canDeactivate()` method can return synchronously; it returns `true` immediately if there is no crisis or there are no pending changes.
But it can also return a `Promise` or an `Observable` and the router will wait for that to resolve to truthy \(navigate\) or falsy \(stay on the current route\).
Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array property.
Now you have given the user a safeguard against unsaved changes.
### *Resolve*: pre-fetching component data
In the `Hero Detail` and `Crisis Detail`, the application waited until the route was activated to fetch the respective hero or crisis.
If you were using a real world API, there might be some delay before the data to display is returned from the server.
You don't want to display a blank component while waiting for the data.
To improve this behavior, you can pre-fetch data from the server using a resolver so it's ready the moment the route is activated.
This also lets you handle errors before routing to the component.
There's no point in navigating to a crisis detail for an `id` that doesn't have a record.
It'd be better to send the user back to the `Crisis List` that shows only valid crisis centers.
In summary, you want to delay rendering the routed component until all necessary data has been fetched.
#### Fetch data before navigating
At the moment, the `CrisisDetailComponent` retrieves the selected crisis.
If the crisis is not found, the router navigates back to the crisis list view.
The experience might be better if all of this were handled first, before the route is activated.
A `crisisDetailResolver` could retrieve a `Crisis` or navigate away, if the `Crisis` did not exist, *before* activating the route and creating the `CrisisDetailComponent`.
Create a `crisis-detail-resolver.ts` file within the `Crisis Center` feature area. This file will contain the `crisisDetailResolver` function.
ng generate resolver crisis-center/crisis-detail-resolver
Move the relevant parts of the crisis retrieval logic in `CrisisDetailComponent.ngOnInit()` into the `crisisDetailResolver`.
Import the `Crisis` model, `CrisisService`, and the `Router` so you can navigate elsewhere if you can't fetch the crisis.
Be explicit and use the `ResolveFn` type with a type of `Crisis`.
Inject the `CrisisService` and `Router`.
That method could return a `Promise`, an `Observable`, or a synchronous return value.
The `CrisisService.getCrisis()` method returns an observable in order to prevent the route from loading until the data is fetched.
If it doesn't return a valid `Crisis`, then return an empty `Observable`, cancel the previous in-progress navigation to the `CrisisDetailComponent`, and navigate the user back to the `CrisisListComponent`.
The updated resolver function looks like this:
Import this resolver in the `crisis-center-routing.module.ts` and add a `resolve` object to the `CrisisDetailComponent` route configuration.
The `CrisisDetailComponent` should no longer fetch the crisis.
When you re-configured the route, you changed where the crisis is.
Update the `CrisisDetailComponent` to get the crisis from the `ActivatedRoute.data.crisis` property instead;
Review the following three important points:
1. The router's `ResolveFn` is optional.
1. The router calls the resolver in any case where the user could navigate away so you don't have to code for each use case.
1. Returning an empty `Observable` in at least one resolver cancels navigation.
The relevant Crisis Center code for this milestone follows.
Guards
### Query parameters and fragments
In the [route parameters](#optional-route-parameters) section, you only dealt with parameters specific to the route.
However, you can use query parameters to get optional parameters available to all routes.
[Fragments](https://en.wikipedia.org/wiki/Fragment_identifier) refer to certain elements on the page identified with an `id` attribute.
Update the `authGuard` to provide a `session_id` query that remains after navigating to another route.
Add an `anchor` element so you can jump to a certain point on the page.
Add the `NavigationExtras` object to the `router.navigate()` method that navigates you to the `/login` route.
You can also preserve query parameters and fragments across navigations without having to provide them again when navigating.
In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigate()` function and provide the `queryParamsHandling` and `preserveFragment` to pass along the current query parameters and fragment to the next route.
The `queryParamsHandling` feature also provides a `merge` option, which preserves and combines the current query parameters with any provided query parameters when navigating.
To navigate to the Admin Dashboard route after logging in, update `admin-dashboard.component.ts` to handle the
query parameters and fragment.
Query parameters and fragments are also available through the `ActivatedRoute` service.
Like route parameters, the query parameters and fragments are provided as an `Observable`.
The updated Crisis Admin component feeds the `Observable` directly into the template using the `AsyncPipe`.
Now, you can click on the Admin button, which takes you to the Login page with the provided `queryParamMap` and `fragment`.
After you click the login button, notice that you have been redirected to the `Admin Dashboard` page with the query parameters and fragment still intact in the address bar.
You can use these persistent bits of information for things that need to be provided across pages like authentication tokens or session ids.
The `query params` and `fragment` can also be preserved using a `RouterLink` with the `queryParamsHandling` and `preserveFragment` bindings respectively.
## Milestone 6: Asynchronous routing
As you've worked through the milestones, the application has naturally gotten larger.
At some point you'll reach a point where the application takes a long time to load.
To remedy this issue, use asynchronous routing, which loads feature modules lazily, on request.
Lazy loading has multiple benefits.
* You can load feature areas only when requested by the user
* You can speed up load time for users that only visit certain areas of the application
* You can continue expanding lazy loaded feature areas without increasing the size of the initial load bundle
You're already part of the way there.
By organizing the application into modules —`AppModule`, `HeroesModule`, `AdminModule`, and `CrisisCenterModule`— you have natural candidates for lazy loading.
Some modules, like `AppModule`, must be loaded from the start.
But others can and should be lazy loaded.
The `AdminModule`, for example, is needed by a few authorized users, so you should only load it when requested by the right people.
### Lazy Loading route configuration
Change the `admin` path in the `admin-routing.module.ts` from `'admin'` to an empty string, `''`, the empty path.
Use empty path routes to group routes together without adding any additional path segments to the URL.
Users will still visit `/admin` and the `AdminComponent` still serves as the Routing Component containing child routes.
Open the `AppRoutingModule` and add a new `admin` route to its `appRoutes` array.
Give it a `loadChildren` property instead of a `children` property.
The `loadChildren` property takes a function that returns a promise using the browser's built-in syntax for lazy loading code using dynamic imports `import('...')`.
The path is the location of the `AdminModule` \(relative to the application root\).
After the code is requested and loaded, the `Promise` resolves an object that contains the `NgModule`, in this case the `AdminModule`.
**NOTE**:
When using absolute paths, the `NgModule` file location must begin with `src/app` in order to resolve correctly.
For custom [path mapping with absolute paths](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping), you must configure the `baseUrl` and `paths` properties in the project `tsconfig.json`.
When the router navigates to this route, it uses the `loadChildren` string to dynamically load the `AdminModule`.
Then it adds the `AdminModule` routes to its current route configuration.
Finally, it loads the requested route to the destination admin component.
The lazy loading and re-configuration happen just once, when the route is first requested; the module and routes are available immediately for subsequent requests.
Take the final step and detach the admin feature set from the main application.
The root `AppModule` must neither load nor reference the `AdminModule` or its files.
In `app.module.ts`, remove the `AdminModule` import statement from the top of the file and remove the `AdminModule` from the NgModule's `imports` array.
### `canMatch`: guarding unauthorized access of feature modules
You're already protecting the `AdminModule` with a `canActivate` guard that prevents unauthorized users from accessing the admin feature area.
It redirects to the login page if the user is not authorized.
But the router is still loading the `AdminModule` even if the user can't visit any of its components.
Ideally, you'd only load the `AdminModule` if the user is logged in.
A `canMatch` guard controls whether the `Router` attempts to match a `Route`. This lets you have
multiple `Route` configurations that share the same `path` but are matched based on different conditions. This approach
allows the `Router` to match the wildcard `Route` instead.
The existing `authGuard` contains the logic to support the `canMatch` guard.
Finally, add the `authGuard` to the `canMatch` array property for the `admin` route.
The completed admin route looks like this:
### Preloading: background loading of feature areas
In addition to loading modules on-demand, you can load modules asynchronously with preloading.
The `AppModule` is eagerly loaded when the application starts, meaning that it loads right away.
Now the `AdminModule` loads only when the user clicks on a link, which is called lazy loading.
Preloading lets you load modules in the background so that the data is ready to render when the user activates a particular route.
Consider the Crisis Center.
It isn't the first view that a user sees.
By default, the Heroes are the first view.
For the smallest initial payload and fastest launch time, you should eagerly load the `AppModule` and the `HeroesModule`.
You could lazy load the Crisis Center.
But you're almost certain that the user will visit the Crisis Center within minutes of launching the app.
Ideally, the application would launch with just the `AppModule` and the `HeroesModule` loaded and then, almost immediately, load the `CrisisCenterModule` in the background.
By the time the user navigates to the Crisis Center, its module is loaded and ready.
#### How preloading works
After each successful navigation, the router looks in its configuration for an unloaded module that it can preload.
Whether it preloads a module, and which modules it preloads, depends upon the preload strategy.
The `Router` offers two preloading strategies:
| Strategies | Details |
|:--- |:--- |
| No preloading | The default. Lazy loaded feature areas are still loaded on-demand. |
| Preloading | All lazy loaded feature areas are preloaded. |
The router either never preloads, or preloads every lazy loaded module.
The `Router` also supports [custom preloading strategies](#custom-preloading) for fine control over which modules to preload and when.
This section guides you through updating the `CrisisCenterModule` to load lazily by default and use the `PreloadAllModules` strategy to load all lazy loaded modules.
#### Lazy load the crisis center
Update the route configuration to lazy load the `CrisisCenterModule`.
Take the same steps you used to configure `AdminModule` for lazy loading.
1. Change the `crisis-center` path in the `CrisisCenterRoutingModule` to an empty string.
1. Add a `crisis-center` route to the `AppRoutingModule`.
1. Set the `loadChildren` string to load the `CrisisCenterModule`.
1. Remove all mention of the `CrisisCenterModule` from `app.module.ts`.
Here are the updated modules *before enabling preload*:
You could try this now and confirm that the `CrisisCenterModule` loads after you click the "Crisis Center" button.
To enable preloading of all lazy loaded modules, import the `PreloadAllModules` token from the Angular router package.
The second argument in the `RouterModule.forRoot()` method takes an object for additional configuration options.
The `preloadingStrategy` is one of those options.
Add the `PreloadAllModules` token to the `forRoot()` call:
This configures the `Router` preloader to immediately load all lazy loaded routes (routes with a `loadChildren` property).
When you visit `http://localhost:4200`, the `/heroes` route loads immediately upon launch and the router starts loading the `CrisisCenterModule` right after the `HeroesModule` loads.
### Custom Preloading Strategy
Preloading every lazy loaded module works well in many situations.
However, in consideration of things such as low bandwidth and user metrics, you can use a custom preloading strategy for specific feature modules.
This section guides you through adding a custom strategy that only preloads routes whose `data.preload` flag is set to `true`.
Recall that you can add anything to the `data` property of a route.
Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModule`.
Generate a new `SelectivePreloadingStrategy` service.
ng generate service selective-preloading-strategy
Replace the contents of `selective-preloading-strategy.service.ts` with the following:
`SelectivePreloadingStrategyService` implements the `PreloadingStrategy`, which has one method, `preload()`.
The router calls the `preload()` method with two arguments:
1. The route to consider.
1. A loader function that can load the routed module asynchronously.
An implementation of `preload` must return an `Observable`.
If the route does preload, it returns the observable returned by calling the loader function.
If the route does not preload, it returns an `Observable` of `null`.
In this sample, the `preload()` method loads the route if the route's `data.preload` flag is truthy. We also skip loading the
`Route` if there is a `canMatch` guard because the user might
not have access to it.
As a side effect, `SelectivePreloadingStrategyService` logs the `path` of a selected route in its public `preloadedModules` array.
Shortly, you'll extend the `AdminDashboardComponent` to inject this service and display its `preloadedModules` array.
But first, make a few changes to the `AppRoutingModule`.
1. Import `SelectivePreloadingStrategyService` into `AppRoutingModule`.
1. Replace the `PreloadAllModules` strategy in the call to `forRoot()` with this `SelectivePreloadingStrategyService`.
Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
1. Import the `SelectivePreloadingStrategyService`.
1. Inject it into the dashboard's constructor.
1. Update the template to display the strategy service's `preloadedModules` array.
Now the file is as follows:
Once the application loads the initial route, the `CrisisCenterModule` is preloaded.
Verify this by logging in to the `Admin` feature area and noting that the `crisis-center` is listed in the `Preloaded Modules`.
It also logs to the browser's console.
### Migrating URLs with redirects
You've set up the routes for navigating around your application and used navigation imperatively and declaratively.
But like any application, requirements change over time.
You've setup links and navigation to `/heroes` and `/hero/:id` from the `HeroListComponent` and `HeroDetailComponent` components.
If there were a requirement that links to `heroes` become `superheroes`, you would still want the previous URLs to navigate correctly.
You also don't want to update every link in your application, so redirects makes refactoring routes trivial.
#### Changing `/heroes` to `/superheroes`
This section guides you through migrating the `Hero` routes to new URLs.
The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed.
To support this change, add redirects from the old routes to the new routes in the `heroes-routing.module`.
Notice two different types of redirects.
The first change is from `/heroes` to `/superheroes` without any parameters.
The second change is from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter.
Router redirects also use powerful pattern-matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination.
Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
The `Router` also supports [query parameters](#query-parameters) and the [fragment](#fragment) when using redirects.
* When using absolute redirects, the `Router` uses the query parameters and the fragment from the `redirectTo` in the route config
* When using relative redirects, the `Router` use the query params and the fragment from the source URL
Currently, the empty path route redirects to `/heroes`, which redirects to `/superheroes`.
This won't work because the `Router` handles redirects once at each level of routing configuration.
This prevents chaining of redirects, which can lead to endless redirect loops.
Instead, update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
A `routerLink` isn't tied to route configuration, so update the associated router links to remain active when the new route is active.
Update the `app.component.ts` template for the `/heroes` `routerLink`.
Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters.
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
### Inspect the router's configuration
To determine if your routes are actually evaluated [in the proper order](#routing-module-order), you can inspect the router's configuration.
Do this by injecting the router and logging to the console its `config` property.
For example, update the `AppModule` as follows and look in the browser console window to see the finished route configuration.
## Final application
For the completed router application, see the for the final source code.
@reviewed 2022-02-28