docs(router): fix markdown, and example mixin cleanup (#2101)
* docs(router): fix markdown and example mixin cleanup - Fixed: markdown text not under `:marked` region (and so that text was not showing up in the generated html). - Fixed: code excerpt title `constructor` -> `isSelected`. - Cleanup of all makeExample mixin uses. This cleanup will help make it easier to record differences with the deprecated router chapter (which must have app-relative makeExample paths). * post-review updates Found another instance of markdown (a heading) outside of a `:marked` region.
This commit is contained in:
parent
a771a6e0d0
commit
ea457825b8
|
@ -98,14 +98,14 @@ mixin makeExample(_filePath, region, _title, stylePatterns)
|
|||
//- ending is given or is just (), then the title will be suffixed with
|
||||
//- either "(excerpt)", or "(#{_region})" when _region is defined.
|
||||
mixin makeExcerpt(_filePath, _region, _title, stylePatterns)
|
||||
- var matches = _filePath.match(/(.*)\s+\(([\w ]*)\)$/);
|
||||
- var matches = _filePath.match(/(.*)\s+\(([^\)]*)\)$/);
|
||||
- var parenText;
|
||||
- if (matches) { _filePath = matches[1]; parenText = matches[2]; }
|
||||
- var adjustments = adjustExamplePathAndTitle({filePath:_filePath, title:_title});
|
||||
- var filePath = adjustments.filePath;
|
||||
- var title = adjustments.title;
|
||||
- var region = _region || parenText;
|
||||
- var excerpt = !region || parenText === '' ? 'excerpt' : parenText || region;
|
||||
- var region = _region || (_region === '' ? '' : parenText);
|
||||
- var excerpt = parenText || region || 'excerpt';
|
||||
- if (title) title = title + ' (' + excerpt + ')';
|
||||
+makeExample(filePath, region, title, stylePatterns)(format='.')
|
||||
|
||||
|
|
|
@ -72,7 +72,8 @@ include ../_util-fns
|
|||
|
||||
If the `app` folder is the application root, as it is for our sample application,
|
||||
set the `href` value *exactly* as shown here.
|
||||
+makeExample('router/ts/index.1.html','base-href', 'index.html (base href)')(format=".")
|
||||
|
||||
+makeExcerpt('index.1.html', 'base-href')
|
||||
|
||||
:marked
|
||||
### Router imports
|
||||
|
@ -80,7 +81,8 @@ include ../_util-fns
|
|||
It is not part of the Angular 2 core. It is in its own library package, `@angular/router`.
|
||||
We import what we need from it as we would from any other Angular package.
|
||||
|
||||
+makeExample('router/ts/app/app.routing.ts','import-router', 'app/app.routing.ts (import)')(format=".")
|
||||
+makeExcerpt('app/app.routing.ts (import)', 'import-router')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We cover other options in the [details below](#browser-url-styles).
|
||||
|
@ -93,7 +95,8 @@ include ../_util-fns
|
|||
We bootstrap our application with an array of routes that we'll provide to our **`RouterModule.forRoot`** function.
|
||||
|
||||
In the following example, we configure our application with four route definitions.
|
||||
+makeExample('router/ts/app/app.routing.1.ts','route-config','app/app.routing.ts')(format='.')
|
||||
|
||||
+makeExcerpt('app/app.routing.1.ts (excerpt)', 'route-config')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -122,13 +125,15 @@ include ../_util-fns
|
|||
a configured *Router* module to our root NgModule imports.
|
||||
:marked
|
||||
Next we open `app.module.ts` where we must register our routing, routing providers, and declare our two route components.
|
||||
+makeExample('router/ts/app/app.module.1.ts','router-basics','app/app.module.ts (basic setup)')(format='.')
|
||||
|
||||
+makeExcerpt('app/app.module.1.ts (basic setup)', 'router-basics')
|
||||
|
||||
:marked
|
||||
### Router Outlet
|
||||
Given this configuration, when the browser URL for this application becomes `/heroes`,
|
||||
the router matches that URL to the `Route` path `/heroes` and displays the `HeroListComponent`
|
||||
in a **`RouterOutlet`** that we've placed in the host view's HTML.
|
||||
code-example(format="", language="html").
|
||||
code-example(language="html").
|
||||
<!-- Routed views go here -->
|
||||
<router-outlet></router-outlet>
|
||||
:marked
|
||||
|
@ -150,7 +155,9 @@ code-example(format="", language="html").
|
|||
or on its parent element.
|
||||
|
||||
We see such bindings in the following `AppComponent` template:
|
||||
+makeExample('router/ts/app/app.component.1.ts', 'template')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.1.ts', 'template', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We're adding two anchor tags with `RouterLink` and `RouterLinkActive` directives.
|
||||
|
@ -257,6 +264,7 @@ table
|
|||
We gloss over everything in between.
|
||||
|
||||
The full source is available in the <live-example></live-example>.
|
||||
|
||||
:marked
|
||||
Our client is the Hero Employment Agency.
|
||||
Heroes need work and The Agency finds Crises for them to solve.
|
||||
|
@ -336,7 +344,7 @@ figure.image-display
|
|||
figure.image-display
|
||||
img(src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" )
|
||||
|
||||
<a id="base-href"></a>
|
||||
a#base-href
|
||||
:marked
|
||||
### Set the *<base href>*
|
||||
The Component Router uses the browser's
|
||||
|
@ -356,7 +364,8 @@ figure.image-display
|
|||
If the `app` folder is the application root, as it is for our application,
|
||||
set the `href` value in **`index.html`** *exactly* as shown here.
|
||||
|
||||
+makeExample('router/ts/index.1.html','base-href', 'index.html (base href)')(format=".")
|
||||
+makeExcerpt('index.1.html', 'base-href')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
HTML 5 style navigation is the Component Router default.
|
||||
|
@ -398,7 +407,7 @@ figure.image-display
|
|||
which returns a module containing the configured `Router` service provider ... and some other,
|
||||
unseen providers that the routing library requires. We export this as the `routing` token.
|
||||
|
||||
+makeExample('router/ts/app/app.routing.2.ts','', 'app/app.routing.ts')(format=".")
|
||||
+makeExcerpt('app/app.routing.2.ts')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -443,7 +452,9 @@ h4#register-providers Register routing in the root NgModule
|
|||
so they will be registered within our root NgModule.
|
||||
|
||||
We also import the `appRoutingProviders` array and add it to the `providers` array.
|
||||
+makeExample('router/ts/app/app.module.1.ts','', 'app.module.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.module.1.ts')
|
||||
|
||||
:marked
|
||||
Providing the router module in our root NgModule makes the Router available everywhere in our application.
|
||||
|
||||
|
@ -457,10 +468,13 @@ figure.image-display
|
|||
a#shell-template
|
||||
:marked
|
||||
The corresponding component template looks like this:
|
||||
+makeExample('router/ts/app/app.component.1.ts','template')(format=".")
|
||||
|
||||
h3#router-outlet <i>RouterOutlet</i>
|
||||
+makeExcerpt('app/app.component.1.ts', 'template', '')
|
||||
|
||||
a#router-outlet
|
||||
:marked
|
||||
### *RouterOutlet*
|
||||
|
||||
`RouterOutlet` is a component from the router library.
|
||||
The router displays views within the bounds of the `<router-outlet>` tags.
|
||||
|
||||
|
@ -469,8 +483,10 @@ h3#router-outlet <i>RouterOutlet</i>
|
|||
A template may hold exactly one ***unnamed*** `<router-outlet>`.
|
||||
The router supports multiple *named* outlets, a feature we'll cover in future.
|
||||
|
||||
h3#router-link <i>RouterLink</i> binding
|
||||
a#router-link
|
||||
:marked
|
||||
### *RouterLink* binding
|
||||
|
||||
Above the outlet, within the anchor tags, we see [Property Bindings](template-syntax.html#property-binding) to
|
||||
the `RouterLink` directive that look like `routerLink="..."`. We imported `RouterLink` from the router library.
|
||||
|
||||
|
@ -506,7 +522,8 @@ h3#router-directives <i>Router Directives</i>
|
|||
They are readily available for us to use in our template.
|
||||
:marked
|
||||
The current state of `app.component.ts` looks like this:
|
||||
+makeExample('router/ts/app/app.component.1.ts','', 'app/app.component.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.1.ts')
|
||||
|
||||
:marked
|
||||
### "Getting Started" wrap-up
|
||||
|
@ -545,6 +562,7 @@ h3#router-directives <i>Router Directives</i>
|
|||
.file typings.json
|
||||
:marked
|
||||
Here are the files discussed in this milestone
|
||||
|
||||
+makeTabs(
|
||||
`router/ts/app/app.component.1.ts,
|
||||
router/ts/app/app.module.1.ts,
|
||||
|
@ -562,9 +580,10 @@ h3#router-directives <i>Router Directives</i>
|
|||
crisis-list.component.ts,
|
||||
index.html`)
|
||||
|
||||
h2#heroes-feature Milestone #2: The Heroes Feature
|
||||
.l-main-section
|
||||
.l-main-section#heroes-feature
|
||||
:marked
|
||||
## Milestone #2: The Heroes Feature
|
||||
|
||||
We've seen how to navigate using the `RouterLink` directive.
|
||||
|
||||
Now we'll learn some new tricks such as how to
|
||||
|
@ -611,7 +630,8 @@ figure.image-display
|
|||
so its available to all components within our module.
|
||||
|
||||
Our `Heroes` module is ready for routing.
|
||||
+makeExample('router/ts/app/heroes/heroes.module.1.ts','', 'app/heroes/heroes.module.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/heroes.module.1.ts')
|
||||
|
||||
:marked
|
||||
When we're done organizing, we have four *Hero Management* files:
|
||||
|
@ -645,7 +665,9 @@ figure.image-display
|
|||
We recommend giving each feature area its own route configuration file.
|
||||
|
||||
Create a new `heroes.routing.ts` in the `heroes` folder like this:
|
||||
+makeExample('router/ts/app/heroes/heroes.routing.ts','', 'app/heroes/heroes.routing.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/heroes.routing.ts')
|
||||
|
||||
:marked
|
||||
We use the same techniques we learned for `app.routing.ts`.
|
||||
|
||||
|
@ -661,12 +683,15 @@ figure.image-display
|
|||
our feature-specific routes without modifying our main route configuration.
|
||||
|
||||
We import our `heroesRouting` token from `heroes.routing.ts` into our `Heroes` module and register the routing.
|
||||
+makeExample('router/ts/app/heroes/heroes.module.ts', 'heroes-routes', 'app/heroes/heroes.module.ts (Heroes routing)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/heroes.module.ts (heroes routing)', 'heroes-routes')
|
||||
|
||||
:marked
|
||||
### Route definition with a parameter
|
||||
The route to `HeroDetailComponent` has a twist.
|
||||
+makeExample('router/ts/app/heroes/heroes.routing.ts','hero-detail-route')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/heroes.routing.ts (excerpt)', 'hero-detail-route', '')
|
||||
|
||||
:marked
|
||||
Notice the `:id` token in the path. That creates a slot in the path for a **Route Parameter**.
|
||||
In this case, we're expecting the router to insert the `id` of a hero into that slot.
|
||||
|
@ -688,8 +713,10 @@ code-example(format="." language="bash").
|
|||
|
||||
An [optional-route-parameter](#optional-route-parameter) might be a better choice if we were passing an *optional* value to `HeroDetailComponent`.
|
||||
|
||||
h3#navigate Navigate to hero detail imperatively
|
||||
a#navigate
|
||||
:marked
|
||||
### Navigate to hero detail imperatively
|
||||
|
||||
*We won't navigate to the detail component by clicking a link*
|
||||
so we won't be adding a new `RouterLink` anchor tag to the shell.
|
||||
|
||||
|
@ -698,15 +725,21 @@ h3#navigate Navigate to hero detail imperatively
|
|||
|
||||
We'll adjust the `HeroListComponent` to implement these tasks, beginning with its constructor
|
||||
which acquires the router service and the `HeroService` by dependency injection:
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','ctor', 'app/heroes/hero-list.component.ts (Constructor)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts (constructor)', 'ctor')
|
||||
|
||||
:marked
|
||||
We make a few changes to the template:
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','template')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts', 'template', '')
|
||||
|
||||
:marked
|
||||
The template defines an `*ngFor` repeater such as [we've seen before](displaying-data.html#ngFor).
|
||||
There's a `(click)` [EventBinding](template-syntax.html#event-binding) to the component's `onSelect` method
|
||||
which we implement as follows:
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','select')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts', 'select', '')
|
||||
|
||||
:marked
|
||||
It calls the router's **`navigate`** method with a **Link Parameters Array**. We can use this same syntax
|
||||
with a `RouterLink` if we want to use it in HTML rather than code.
|
||||
|
@ -718,13 +751,19 @@ h3#route-parameters Setting the route parameters in the list view
|
|||
|
||||
Accordingly, the *link parameters array* has *two* items: the **path** of the destination route and a **route parameter** that specifies the
|
||||
`id` of the selected hero.
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts', 'link-parameters-array', '')
|
||||
|
||||
:marked
|
||||
The router composes the appropriate two-part destination URL from this array:
|
||||
code-example(format="." language="bash").
|
||||
|
||||
code-example(language="bash").
|
||||
localhost:3000/hero/15
|
||||
h3#get-route-parameter Getting the route parameter in the details view
|
||||
|
||||
a#get-route-parameter
|
||||
:marked
|
||||
### Getting the route parameter in the details view
|
||||
|
||||
How does the target `HeroDetailComponent` learn about that `id`?
|
||||
Certainly not by analyzing the URL! That's the router's job.
|
||||
|
||||
|
@ -735,14 +774,18 @@ a#hero-detail-ctor
|
|||
:marked
|
||||
As usual, we write a constructor that asks Angular to inject services
|
||||
that the component requires and reference them as private variables.
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','ctor', 'app/heroes/hero-detail.component.ts (Constructor)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.ts (constructor)', 'ctor')
|
||||
|
||||
:marked
|
||||
Later, in the `ngOnInit` method,
|
||||
we use the `ActivatedRoute` service to retrieve the parameters for our route.
|
||||
Since our parameters are provided as an `Observable`, we _subscribe_ to them for the `id` parameter by name and
|
||||
tell the `HeroService` to fetch the hero with that `id`. We'll keep a reference to this `Subscription` so we can
|
||||
tidy things up later.
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','ngOnInit')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.ts', 'ngOnInit', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent`.
|
||||
|
@ -759,7 +802,8 @@ a#hero-detail-ctor
|
|||
*Failure to do so could create a memory leak.*
|
||||
|
||||
We unsubscribe from our `Observable` in the `ngOnDestroy` method.
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','ngOnDestroy')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.ts', 'ngOnDestroy', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -801,7 +845,9 @@ h4#snapshot <i>Snapshot</i>: the no-observable alternative
|
|||
The router offers a *Snapshot* alternative that gives us the initial value of the route parameters.
|
||||
We don't need to subscribe. We don't have to unsubscribe in `ngOnDestroy`.
|
||||
It's much simpler to write and read:
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.2.ts','snapshot')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.2.ts (excerpt)', 'snapshot', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
**Remember:** we only get the _initial_ value of the parameters with this technique.
|
||||
|
@ -809,15 +855,18 @@ h4#snapshot <i>Snapshot</i>: the no-observable alternative
|
|||
to this component multiple times in a row.
|
||||
We are leaving the observable `params` strategy in place just in case.
|
||||
|
||||
h3#nav-to-list Navigating back to the list component
|
||||
a#nav-to-list
|
||||
:marked
|
||||
### Navigating back to the list component
|
||||
|
||||
The `HeroDetailComponent` has a "Back" button wired to its `gotoHeroes` method that navigates imperatively
|
||||
back to the `HeroListComponent`.
|
||||
|
||||
The router `navigate` method takes the same one-item *link parameters array*
|
||||
that we can bind to a `[routerLink]` directive.
|
||||
It holds the **path to the `HeroListComponent`**:
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.1.ts (excerpt)', 'gotoHeroes', '')
|
||||
|
||||
h3#merge-hero-routes Import hero module into root NgModule
|
||||
:marked
|
||||
|
@ -826,7 +875,7 @@ h3#merge-hero-routes Import hero module into root NgModule
|
|||
|
||||
Update `app.module.ts` as follows:
|
||||
|
||||
+makeExample('router/ts/app/app.module.2.ts','hero-import', 'app.module.ts (Heroes module import)')(format=".")
|
||||
+makeExcerpt('app/app.module.2.ts (heroes module import)', 'hero-import')
|
||||
|
||||
:marked
|
||||
We imported the `HeroesModule` and added it to our root NgModule `imports`.
|
||||
|
@ -841,7 +890,7 @@ h3#merge-hero-routes Import hero module into root NgModule
|
|||
|
||||
Since our `Heroes` routes are defined within our submodule, we can also remove our initial `heroes` route from the `app.routing.ts`.
|
||||
|
||||
+makeExample('router/ts/app/app.routing.3.ts','', 'app.routing.ts (v.2)')(format=".")
|
||||
+makeExcerpt('app/app.routing.3.ts (v2)', '')
|
||||
|
||||
:marked
|
||||
### Heroes App Wrap-up
|
||||
|
@ -883,6 +932,7 @@ h3#merge-hero-routes Import hero module into root NgModule
|
|||
<a id="heroes-app-code"></a>
|
||||
### The Heroes App code
|
||||
Here are the relevant files for this version of the sample application.
|
||||
|
||||
+makeTabs(
|
||||
`router/ts/app/app.component.1.ts,
|
||||
router/ts/app/app.module.2.ts,
|
||||
|
@ -901,12 +951,11 @@ h3#merge-hero-routes Import hero module into root NgModule
|
|||
hero.service.ts,
|
||||
heroes.module.ts,
|
||||
heroes.routing.ts`)
|
||||
:marked
|
||||
|
||||
<a id="crisis-center-feature"></a>
|
||||
.l-main-section
|
||||
.l-main-section#crisis-center-feature
|
||||
:marked
|
||||
## Milestone #3: The Crisis Center
|
||||
|
||||
The *Crisis Center* is a fake view at the moment. Time to make it useful.
|
||||
|
||||
The new *Crisis Center* begins as a virtual copy of the *Heroes* module.
|
||||
|
@ -962,10 +1011,14 @@ h3#merge-hero-routes Import hero module into root NgModule
|
|||
figure.image-display
|
||||
img(src='/resources/images/devguide/router/component-tree.png' alt="Component Tree" )
|
||||
|
||||
h3#child-routing-component Child Routing Component
|
||||
a#child-routing-component
|
||||
:marked
|
||||
### Child Routing Component
|
||||
|
||||
Add the following `crisis-center.component.ts` to the `crisis-center` folder:
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)')(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.component.ts (minus imports)', 'minus-imports')
|
||||
|
||||
:marked
|
||||
The `CrisisCenterComponent` is much like the `AppComponent` shell.
|
||||
|
||||
|
@ -981,6 +1034,7 @@ h3#child-routing-component Child Routing Component
|
|||
Unlike `AppComponent` (and most other components), it **lacks a selector**.
|
||||
It doesn't need one. We don't *embed* this component in a parent template.
|
||||
We *navigate* to it from the outside, via the router.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We *can* give it a selector. There's no harm in it.
|
||||
|
@ -993,7 +1047,9 @@ h3#child-routing-component Child Routing Component
|
|||
Instead of registering it with the root NgModule's providers —
|
||||
which makes it visible everywhere —
|
||||
we register the `CrisisService` in the `CrisisCenterModule` providers array.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.module.1.ts', 'providers')(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.module.1.ts', 'providers', '')
|
||||
|
||||
:marked
|
||||
This limits the scope of the `CrisisService` to the *Crisis Center* routes.
|
||||
No module outside of the *Crisis Center* can access it.
|
||||
|
@ -1011,12 +1067,15 @@ h3#child-routing-component Child Routing Component
|
|||
|
||||
:marked
|
||||
### Child Route Configuration
|
||||
|
||||
The `CrisisCenterComponent` is a *Routing Component* like the `AppComponent`.
|
||||
It has its own `RouterOutlet` and its own child routes.
|
||||
|
||||
We create a `crisis-center.routing.ts` file as we did the `heroes.routing.ts` file.
|
||||
But this time we define **child routes** *within* the parent `crisis-center` route.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.1.ts', 'routes', 'app/crisis-center/crisis-center.routing.ts (Routes)' )(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.1.ts (Routes)', 'routes')
|
||||
|
||||
:marked
|
||||
Notice that the parent `crisis-center` route has a `children` property
|
||||
with an array of two routes.
|
||||
|
@ -1039,30 +1098,36 @@ h3#child-routing-component Child Routing Component
|
|||
To write an URL that navigates to the `CrisisDetailComponent`, we'd append the child route path, `/`,
|
||||
followed by the crisis id, yielding something like:
|
||||
|
||||
code-example(format="").
|
||||
code-example.
|
||||
localhost:3000/crisis-center/2
|
||||
|
||||
:marked
|
||||
Here's the complete `crisis-center.routing.ts` with its imports.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.1.ts', '', 'app/crisis-center/crisis-center.routing.ts' )(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.1.ts', '')
|
||||
|
||||
h3#import-crisis-module Import crisis center module into the root NgModule routes
|
||||
:marked
|
||||
As with the `Heroes` module, we must import the `Crisis Center` module into the root NgModule:
|
||||
+makeExample('router/ts/app/app.module.3.ts', '', 'app/app.module.ts (Crisis Center Module)' )(format='.')
|
||||
|
||||
+makeExcerpt('app/app.module.3.ts (Crisis Center Module)', '')
|
||||
|
||||
:marked
|
||||
We also remove the initial crisis center route from our `app.routing.ts`. Our routes
|
||||
are now being provided by our `HeroesModule` and our `CrisisCenter` submodules. We'll keep our `app.routing.ts` file
|
||||
for general routes which we'll cover later in the chapter.
|
||||
|
||||
+makeExample('router/ts/app/app.routing.4.ts', '', 'app/app.routing.ts (v.3)' )(format='.')
|
||||
+makeExcerpt('app/app.routing.4.ts (v3)', '')
|
||||
|
||||
a#redirect
|
||||
h3#redirect Redirecting routes
|
||||
:marked
|
||||
### Redirecting routes
|
||||
|
||||
When the application launches, the initial URL in the browser bar is something like:
|
||||
code-example(format="").
|
||||
|
||||
code-example.
|
||||
localhost:3000
|
||||
|
||||
:marked
|
||||
That doesn't match any of our configured routes which means that our application won't display any component when it's launched.
|
||||
The user must click one of the navigation links to trigger a navigation and display something.
|
||||
|
@ -1072,7 +1137,8 @@ code-example(format="").
|
|||
|
||||
The preferred solution is to add a `redirect` route that transparently translates from the initial relative URL (`''`)
|
||||
to the desired default path (`/crisis-center`):
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.2.ts', 'redirect', 'app/crisis-center/crisis-center.routing.ts (redirect route)' )(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.2.ts' , 'redirect', '')
|
||||
|
||||
:marked
|
||||
A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route.
|
||||
|
@ -1102,7 +1168,8 @@ code-example(format="").
|
|||
|
||||
:marked
|
||||
The updated route definitions look like this:
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.2.ts', 'routes', 'app/crisis-center/crisis-center.routing.ts (Routes v.2)' )(format='.')
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.2.ts (routes v2)' , 'routes')
|
||||
|
||||
.l-main-section
|
||||
h2#guards Route Guards
|
||||
|
@ -1153,15 +1220,16 @@ h2#guards Route Guards
|
|||
|
||||
Let's look at some examples.
|
||||
|
||||
.l-main-section
|
||||
// :marked
|
||||
<a id="lifecycle-hooks"></a>
|
||||
.l-main-section#lifecycle-hooks
|
||||
:marked
|
||||
## Router Lifecycle Hooks
|
||||
|
||||
TODO: Pausing activation
|
||||
|
||||
h3#can-activate-guard <i>CanActivate</i>: requiring authentication
|
||||
a#can-activate-guard
|
||||
:marked
|
||||
### *CanActivate*: requiring authentication
|
||||
|
||||
Applications often restrict access to a feature area based on who the user is.
|
||||
We could permit access only to authenticated users or to users with a specific role.
|
||||
We might block or limit access until the user's account is activated.
|
||||
|
@ -1173,13 +1241,17 @@ h3#can-activate-guard <i>CanActivate</i>: requiring authentication
|
|||
We intend to extend the Crisis Center with some new *administrative* features.
|
||||
Those features aren't defined yet. So we add the following placeholder component.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-admin.component.1.ts', '', 'crisis-admin.component.ts')(format=".")
|
||||
+makeExcerpt('app/crisis-center/crisis-admin.component.1.ts')
|
||||
|
||||
:marked
|
||||
Next, we add a child route to the `crisis-center.routes` with the path, `/admin`.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.3.ts', 'admin-route-no-guard', 'crisis-center.routing.ts (admin route)')(format=".")
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.3.ts (admin route)', 'admin-route-no-guard')
|
||||
|
||||
:marked
|
||||
And we add a link to the `AppComponent` shell that users can click to get to this feature.
|
||||
+makeExample('router/ts/app/app.component.4.ts', 'template', 'app/app.component.ts (template)')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.4.ts', 'template')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -1202,19 +1274,26 @@ h3#can-activate-guard <i>CanActivate</i>: requiring authentication
|
|||
|
||||
At the moment we're interested in seeing how guards work so our first version does nothing useful.
|
||||
It simply logs to console and `returns` true immediately, allowing navigation to proceed:
|
||||
+makeExample('router/ts/app/auth-guard.service.1.ts', '', 'app/auth-guard.service.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/auth-guard.service.1.ts')
|
||||
|
||||
:marked
|
||||
Next we open `crisis-center.routing.ts `, import the `AuthGuard` class, and
|
||||
update the admin route with a `CanActivate` guard property that references it:
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.ts', 'admin-route', 'crisis-center.routing.ts (guarded admin route)')(format=".")
|
||||
Our admin feature is now protected by the guard, albeit protected poorly.
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.ts (guarded admin route)', 'admin-route')
|
||||
|
||||
:marked
|
||||
Our admin feature is now protected by the guard, albeit protected poorly.
|
||||
|
||||
#### Teach *AuthGuard* to authenticate
|
||||
Let's make our `AuthGuard` at least pretend to authenticate.
|
||||
|
||||
The `AuthGuard` should call an application service that can login a user and retain information about the current user.
|
||||
Here's a demo `AuthService`:
|
||||
+makeExample('router/ts/app/auth.service.ts', '', 'app/auth.service.ts')(format=".")
|
||||
|
||||
+makeExcerpt('app/auth.service.ts')
|
||||
|
||||
:marked
|
||||
Although it doesn't actually log in, it has what we need for this discussion.
|
||||
It has an `isLoggedIn` flag to tell us whether the user is authenticated.
|
||||
|
@ -1222,7 +1301,9 @@ h3#can-activate-guard <i>CanActivate</i>: requiring authentication
|
|||
The `redirectUrl` property will store our attempted URL so we can navigate to it after authenticating.
|
||||
|
||||
Let's revise our `AuthGuard` to call it.
|
||||
+makeExample('router/ts/app/auth-guard.service.2.ts', '', 'app/auth-guard.service.ts (v.2)')(format=".")
|
||||
|
||||
+makeExcerpt('app/auth-guard.service.2.ts (v2)', '')
|
||||
|
||||
:marked
|
||||
Notice that we *inject* the `AuthService` and the `Router` in the constructor.
|
||||
We haven't provided the `AuthService` yet but it's good to know that we can inject helpful services into our routing guards.
|
||||
|
@ -1295,7 +1376,9 @@ h3#can-deactivate-guard <i>CanDeactivate</i>: handling unsaved changes
|
|||
We discard the changes if the user presses he *Cancel* button.
|
||||
|
||||
Both buttons navigate back to the crisis list after save or cancel.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-detail.component.1.ts', 'cancel-save', 'crisis-detail.component.ts (excerpt)')(format=".")
|
||||
|
||||
+makeExcerpt('app/crisis-center/crisis-detail.component.1.ts (excerpt)', 'cancel-save')
|
||||
|
||||
:marked
|
||||
What if the user tries to navigate away without saving or canceling?
|
||||
The user could push the browser back button or click the heroes link.
|
||||
|
@ -1318,24 +1401,27 @@ h3#can-deactivate-guard <i>CanDeactivate</i>: handling unsaved changes
|
|||
*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`).
|
||||
|
||||
<a id="CanDeactivate"></a>
|
||||
a#CanDeactivate
|
||||
:marked
|
||||
We create a `Guard` that will check for the presence of a `canDeactivate` function in our component, in this
|
||||
case being `CrisisDetailComponent`. We don't need to know the details of how our `CrisisDetailComponent` confirms deactivation.
|
||||
This makes our guard reusable, which is an easy win for us.
|
||||
+makeExample('router/ts/app/can-deactivate-guard.service.ts', '', 'can-deactivate-guard.service.ts')
|
||||
|
||||
+makeExample('app/can-deactivate-guard.service.ts')
|
||||
|
||||
:marked
|
||||
Alternatively, We could make a component-specific `CanDeactivate` guard for our `CrisisDetailComponent`. The `canDeactivate` method provides us
|
||||
with the current instance of our `component`, the current `ActivatedRoute` and `RouterStateSnapshot` in case we needed to access
|
||||
some external information. This would be useful if we only wanted to use this guard for this component and needed to ask the component's
|
||||
properties in or to confirm whether the router should allow navigation away from it.
|
||||
+makeExample('router/ts/app/can-deactivate-guard.service.1.ts', '', 'can-deactivate-guard.service.ts (component-specific)')
|
||||
|
||||
+makeExcerpt('app/can-deactivate-guard.service.1.ts (component-specific)', '')
|
||||
|
||||
:marked
|
||||
Looking back at our `CrisisDetailComponent`, we have implemented our confirmation workflow for unsaved changes.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-detail.component.1.ts', 'cancel-save-only', 'crisis-detail.component.ts (excerpt)')
|
||||
+makeExcerpt('app/crisis-center/crisis-detail.component.1.ts (excerpt)', 'cancel-save-only')
|
||||
|
||||
:marked
|
||||
Notice that the `canDeactivate` method *can* return synchronously;
|
||||
it returns `true` immediately if there is no crisis or there are no pending changes.
|
||||
|
@ -1344,11 +1430,13 @@ h3#can-deactivate-guard <i>CanDeactivate</i>: handling unsaved changes
|
|||
|
||||
:marked
|
||||
We add the `Guard` to our crisis detail route in `crisis-center.routing.ts` using the `canDeactivate` array.
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.4.ts', '', 'crisis-center.routing.ts')
|
||||
|
||||
+makeExample('app/crisis-center/crisis-center.routing.4.ts', '')
|
||||
|
||||
:marked
|
||||
We also need to add the `Guard` to our main `appRoutingProviders` so the `Router` can inject it during the navigation process.
|
||||
+makeExample('router/ts/app/app.routing.5.ts', '', 'app.routing.ts')
|
||||
|
||||
+makeExample('app/app.routing.5.ts', '', '')
|
||||
|
||||
:marked
|
||||
Now we have given our user a safeguard against unsaved changes.
|
||||
|
@ -1387,7 +1475,7 @@ h3#resolve-guard <i>Resolve</i>: pre-fetching component data
|
|||
|
||||
Let's create our `crisis-detail-resolve.service.ts` file within our `Crisis Center` feature area.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-detail-resolve.service.ts', '', 'crisis-detail-resolve.service.ts')
|
||||
+makeExample('app/crisis-center/crisis-detail-resolve.service.ts', '')
|
||||
|
||||
:marked
|
||||
We'll take the relevant parts of the `ngOnInit` lifecycle hook in our `CrisisDetailComponent` and moved them into our `CrisisDetailResolve` guard.
|
||||
|
@ -1401,12 +1489,12 @@ h3#resolve-guard <i>Resolve</i>: pre-fetching component data
|
|||
|
||||
Now that our guard is ready, we'll import it in our `crisis-center.routing.ts` and use the `resolve` object in our route configuration.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.routing.5.ts', 'crisis-detail-resolve', 'crisis-center.routing.ts (resolve)')
|
||||
+makeExcerpt('app/crisis-center/crisis-center.routing.5.ts (resolve)', 'crisis-detail-resolve')
|
||||
|
||||
:marked
|
||||
We'll add the `CrisisDetailResolve` service to our crisis center module's `providers`, so its available to the `Router` during the navigation process.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.module.ts', 'crisis-detail-resolve', 'crisis-center.module.ts (crisis detail resolve provider)')
|
||||
+makeExcerpt('app/crisis-center/crisis-center.module.ts (crisis detail resolve provider)', 'crisis-detail-resolve')
|
||||
|
||||
:marked
|
||||
Now that we've added our `Resolve` guard to fetch data before the route loads, we no longer need to do this once we get into our `CrisisDetailComponent`.
|
||||
|
@ -1414,7 +1502,7 @@ h3#resolve-guard <i>Resolve</i>: pre-fetching component data
|
|||
Once activated, all we need to do is set our local `crisis` and `editName` properties from our resolved `Crisis` information. We no longer need to subscribe
|
||||
and unsubscribe to the `ActivatedRoute` params to fetch the `Crisis` because it is being provided synchronously at the time the route component is activated.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'crisis-detail-resolve', 'crisis-detail.component.ts (ngOnInit v.2)')
|
||||
+makeExcerpt('app/crisis-center/crisis-detail.component.ts (ngOnInit v2)', 'crisis-detail-resolve')
|
||||
|
||||
:marked
|
||||
**Two critical points**
|
||||
|
@ -1455,8 +1543,7 @@ h3#resolve-guard <i>Resolve</i>: pre-fetching component data
|
|||
`)
|
||||
|
||||
|
||||
<a id="optional-route-parameters"></a>
|
||||
.l-main-section
|
||||
.l-main-section#optional-route-parameters
|
||||
:marked
|
||||
## Milestone #4: Route Parameters
|
||||
|
||||
|
@ -1500,18 +1587,24 @@ figure.image-display
|
|||
|
||||
<a id="route-parameters-object"></a>
|
||||
### Route parameter
|
||||
|
||||
When navigating to the `HeroDetailComponent` we specified the `id` of the hero-to-edit in the
|
||||
*route parameter* and made it the second item of the [*link parameters array*](#link-parameters-array).
|
||||
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".")
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts', 'link-parameters-array', '')
|
||||
|
||||
:marked
|
||||
The router embedded the `id` value in the navigation URL because we had defined it
|
||||
as a route parameter with an `:id` placeholder token in the route `path`:
|
||||
+makeExample('router/ts/app/heroes/heroes.routing.ts','hero-detail-route')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/heroes.routing.ts', 'hero-detail-route', '')
|
||||
|
||||
:marked
|
||||
When the user clicks the back button, the `HeroDetailComponent` constructs another *link parameters array*
|
||||
which it uses to navigate back to the `HeroListComponent`.
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.1.ts', 'gotoHeroes', '')
|
||||
|
||||
:marked
|
||||
This array lacks a route parameter because we had no reason to send information to the `HeroListComponent`.
|
||||
|
||||
|
@ -1521,7 +1614,9 @@ figure.image-display
|
|||
We do that with an object that contains our optional `id` parameter.
|
||||
We also defined a junk parameter (`foo`) that the `HeroListComponent` should ignore.
|
||||
Here's the revised navigation statement:
|
||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','gotoHeroes-navigate')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-detail.component.ts', 'gotoHeroes-navigate', '')
|
||||
|
||||
:marked
|
||||
The application still works. Clicking "back" returns to the hero list view.
|
||||
|
||||
|
@ -1532,15 +1627,18 @@ figure.image-display
|
|||
When running in plunker, pop out the preview window by clicking the blue 'X' button in the upper right corner.
|
||||
:marked
|
||||
It should look something like this, depending on where you run it:
|
||||
code-example(format="." language="bash").
|
||||
|
||||
code-example(language="bash").
|
||||
localhost:3000/heroes;id=15;foo=foo
|
||||
|
||||
:marked
|
||||
The `id` value appears in the URL as (`;id=15;foo=foo`), not in the URL path.
|
||||
The path for the "Heroes" route doesn't have an `:id` token.
|
||||
:marked
|
||||
|
||||
The optional route parameters are not separated by "?" and "&".
|
||||
They are **separated by semicolons (;)**
|
||||
This is *matrix URL* notation — something we may not have seen before.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
*Matrix URL* notation is an idea first floated
|
||||
|
@ -1575,23 +1673,31 @@ code-example(format="." language="bash").
|
|||
This time we'll be navigating in the opposite direction, from the `HeroDetailComponent` to the `HeroListComponent`.
|
||||
|
||||
First we extend the router import statement to include the `ActivatedRoute` service symbol;
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','import-router', 'hero-list.component.ts (import)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.ts (import)', 'import-router')
|
||||
|
||||
:marked
|
||||
Then we use the `ActivatedRoute` to access the `params` _Observable_ so we can subscribe
|
||||
and extract the `id` parameter as the `selectedId`:
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','ctor', 'hero-list.component.ts (constructor)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.ts (constructor)', 'ctor')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
All route/query parameters are strings.
|
||||
The (+) in front of the `params['id']` expression is a JavaScript trick to convert the string to an integer.
|
||||
:marked
|
||||
We add an `isSelected` method that returns true when a hero's id matches the selected id.
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','isSelected', 'hero-list.component.ts (constructor)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.ts', 'isSelected')
|
||||
|
||||
:marked
|
||||
Finally, we update our template with a [Class Binding](template-syntax.html#class-binding) to that `isSelected` method.
|
||||
The binding adds the `selected` CSS class when the method returns `true` and removes it when `false`.
|
||||
Look for it within the repeated `<li>` tag as shown here:
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','template', 'hero-list.component.ts (template)')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.ts', 'template')
|
||||
|
||||
:marked
|
||||
When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected:
|
||||
figure.image-display
|
||||
|
@ -1599,11 +1705,11 @@ figure.image-display
|
|||
:marked
|
||||
The optional `foo` route parameter is harmless and continues to be ignored.
|
||||
|
||||
<a id="query-parameters"></a>
|
||||
<a id="fragment"></a>
|
||||
a#query-parameters
|
||||
a#fragment
|
||||
:marked
|
||||
### Query Parameters and Fragments
|
||||
:marked
|
||||
|
||||
In our [route parameters](#optional-route-parameters) example, we only dealt with parameters specific to
|
||||
our route, but what if we wanted optional parameters available to all routes? This is where our
|
||||
query parameters come into play and serve a special purpose in our application.
|
||||
|
@ -1616,7 +1722,8 @@ figure.image-display
|
|||
We'll also provide an arbitrary `anchor` fragment, which we would use to jump to a certain point on our page.
|
||||
|
||||
We'll add the `NavigationExtras` object to our `router.navigate` method that navigates us to our `/login` route.
|
||||
+makeExample('router/ts/app/auth-guard.service.ts','', 'auth-guard.service.ts (v.3)')
|
||||
|
||||
+makeExcerpt('app/auth-guard.service.ts (v3)', '')
|
||||
|
||||
:marked
|
||||
We can also **preserve** query parameters and fragments across navigations without having to re-provide them
|
||||
|
@ -1624,13 +1731,14 @@ figure.image-display
|
|||
and provide the `preserveQueryParams` and `preserveFragment` to pass along the current query parameters
|
||||
and fragment to the next route.
|
||||
|
||||
+makeExample('router/ts/app/login.component.ts','preserve', 'login.component.ts (preserved)')(format=".")
|
||||
+makeExcerpt('app/login.component.ts', 'preserve')
|
||||
|
||||
:marked
|
||||
Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our
|
||||
query parameters and fragment.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-admin.component.ts','', 'crisis-admin.component.ts (v.2)')
|
||||
+makeExcerpt('app/crisis-center/crisis-admin.component.ts (v2)', '')
|
||||
|
||||
:marked
|
||||
*Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components.
|
||||
Just like our *route parameters*, query parameters and fragments are provided as an `Observable`.
|
||||
|
@ -1696,7 +1804,7 @@ figure.image-display
|
|||
out our `Crisis Center` feature area. After the path to the file we use a `#` to denote where our file path ends and to tell the `Router` the name
|
||||
of our `CrisisCenter` NgModule. If we look in our `crisis-center.module` file, we can see it matches name of our exported NgModule class.
|
||||
|
||||
+makeExample('router/ts/app/crisis-center/crisis-center.module.ts', 'crisis-center-module-export', 'crisis-center.module.ts (export)')
|
||||
+makeExcerpt('app/crisis-center/crisis-center.module.ts (export)', 'crisis-center-module-export')
|
||||
|
||||
:marked
|
||||
The `loadChildren` property is used by the `Router` to map to our bundle we want to lazy-load, in this case being the `CrisisCenterModule`.
|
||||
|
@ -1714,13 +1822,13 @@ figure.image-display
|
|||
to break our `CrisisCenterModule` into a completely separate module. In our `app.module.ts`, we'll remove our `CrisisCenterModule` from the
|
||||
`imports` array since we'll be loading it on-demand an we'll remove the imported `CrisisCenterModule`.
|
||||
|
||||
+makeExample('router/ts/app/app.module.ts', '', 'app.module.ts (final)')
|
||||
+makeExcerpt('app/app.module.ts (final)', '')
|
||||
|
||||
:marked
|
||||
If our initial redirect went to `/heroes` instead of going to `/crisis-center`, the `CrisisCenterModule` would not be loaded until the user
|
||||
visited a `Crisis Center` route. We'll update our redirect in our `app.routing.ts` to make this change.
|
||||
|
||||
+makeExample('router/ts/app/app.routing.6.ts', 'heroes-redirect', 'app.routing.ts (heroes redirect)')
|
||||
+makeExcerpt('app/app.routing.6.ts (heroes redirect)', 'heroes-redirect')
|
||||
|
||||
<a id="final-app"></a>
|
||||
.l-main-section
|
||||
|
@ -1739,10 +1847,10 @@ figure.image-display
|
|||
The appendix material isn't essential. Continued reading is for the curious.
|
||||
|
||||
|
||||
.l-main-section
|
||||
<a id="link-parameters-array"></a>
|
||||
.l-main-section#link-parameters-array
|
||||
:marked
|
||||
## Appendix: Link Parameters Array
|
||||
|
||||
We've mentioned the *Link Parameters Array* several times. We've used it several times.
|
||||
|
||||
A link parameters array holds the ingredients for router navigation:
|
||||
|
@ -1750,19 +1858,27 @@ figure.image-display
|
|||
* required and optional route parameters that go into the route URL
|
||||
|
||||
We can bind the `RouterLink` directive to such an array like this:
|
||||
+makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.3.ts', 'h-anchor', '')
|
||||
|
||||
:marked
|
||||
We've written a two element array when specifying a route parameter like this
|
||||
+makeExample('router/ts/app/heroes/hero-list.component.1.ts', 'nav-to-detail')(format=".")
|
||||
|
||||
+makeExcerpt('app/heroes/hero-list.component.1.ts', 'nav-to-detail', '')
|
||||
|
||||
:marked
|
||||
We can provide optional route parameters in an object like this:
|
||||
+makeExample('router/ts/app/app.component.3.ts', 'cc-query-params')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.3.ts', 'cc-query-params', '')
|
||||
|
||||
:marked
|
||||
These three examples cover our needs for an app with one level routing.
|
||||
The moment we add a child router, such as the *Crisis Center*, we create new link array possibilities.
|
||||
|
||||
Recall that we specified a default child route for *Crisis Center* so this simple `RouterLink` is fine.
|
||||
+makeExample('router/ts/app/app.component.3.ts', 'cc-anchor-w-default')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.3.ts', 'cc-anchor-w-default', '')
|
||||
|
||||
:marked
|
||||
Let's parse it out.
|
||||
* The first item in the array identifies the parent route ('/crisis-center').
|
||||
|
@ -1782,17 +1898,20 @@ figure.image-display
|
|||
* We add `id` of the *Dragon Crisis* as the second item in the array (`1`)
|
||||
|
||||
It looks like this!
|
||||
+makeExample('router/ts/app/app.component.3.ts', 'Dragon-anchor')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.3.ts', 'Dragon-anchor', '')
|
||||
|
||||
:marked
|
||||
If we wanted to, we could redefine our `AppComponent` template with *Crisis Center* routes exclusively:
|
||||
+makeExample('router/ts/app/app.component.3.ts', 'template')(format=".")
|
||||
|
||||
+makeExcerpt('app/app.component.3.ts', 'template', '')
|
||||
|
||||
:marked
|
||||
In sum, we can write applications with one, two or more levels of routing.
|
||||
The link parameters array affords the flexibility to represent any routing depth and
|
||||
any legal sequence of route paths, (required) router parameters and (optional) route parameter objects.
|
||||
|
||||
<a id="onInit"></a>
|
||||
.l-main-section
|
||||
.l-main-section#onInit
|
||||
:marked
|
||||
## Appendix: Why use an *ngOnInit* method
|
||||
|
||||
|
@ -1890,7 +2009,9 @@ code-example(format=".", language="bash").
|
|||
The preferred way to configure the strategy is to add a
|
||||
[<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag
|
||||
in the `<head>` of the `index.html`.
|
||||
+makeExample('router/ts/index.1.html','base-href')(format=".")
|
||||
|
||||
+makeExcerpt('index.1.html', 'base-href', '')
|
||||
|
||||
:marked
|
||||
Without that tag, the browser may not be able to load resources
|
||||
(images, css, scripts) when "deep linking" into the app.
|
||||
|
@ -1914,4 +2035,5 @@ code-example(format=".", language="bash").
|
|||
We can go old-school with the `HashLocationStrategy` by
|
||||
providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot`
|
||||
in our root NgModule.
|
||||
+makeExample('router/ts/app/app.module.4.ts','', 'app.module.ts (hash URL strategy)')
|
||||
|
||||
+makeExcerpt('app/app.module.4.ts (hash URL strategy)', '')
|
||||
|
|
Loading…
Reference in New Issue