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:
Patrice Chalin 2016-08-12 11:20:44 -07:00 committed by Kathy Walrath
parent a771a6e0d0
commit ea457825b8
2 changed files with 231 additions and 109 deletions

View File

@ -98,14 +98,14 @@ mixin makeExample(_filePath, region, _title, stylePatterns)
//- ending is given or is just (), then the title will be suffixed with //- ending is given or is just (), then the title will be suffixed with
//- either "(excerpt)", or "(#{_region})" when _region is defined. //- either "(excerpt)", or "(#{_region})" when _region is defined.
mixin makeExcerpt(_filePath, _region, _title, stylePatterns) mixin makeExcerpt(_filePath, _region, _title, stylePatterns)
- var matches = _filePath.match(/(.*)\s+\(([\w ]*)\)$/); - var matches = _filePath.match(/(.*)\s+\(([^\)]*)\)$/);
- var parenText; - var parenText;
- if (matches) { _filePath = matches[1]; parenText = matches[2]; } - if (matches) { _filePath = matches[1]; parenText = matches[2]; }
- var adjustments = adjustExamplePathAndTitle({filePath:_filePath, title:_title}); - var adjustments = adjustExamplePathAndTitle({filePath:_filePath, title:_title});
- var filePath = adjustments.filePath; - var filePath = adjustments.filePath;
- var title = adjustments.title; - var title = adjustments.title;
- var region = _region || parenText; - var region = _region || (_region === '' ? '' : parenText);
- var excerpt = !region || parenText === '' ? 'excerpt' : parenText || region; - var excerpt = parenText || region || 'excerpt';
- if (title) title = title + ' (' + excerpt + ')'; - if (title) title = title + ' (' + excerpt + ')';
+makeExample(filePath, region, title, stylePatterns)(format='.') +makeExample(filePath, region, title, stylePatterns)(format='.')

View File

@ -72,7 +72,8 @@ include ../_util-fns
If the `app` folder is the application root, as it is for our sample application, If the `app` folder is the application root, as it is for our sample application,
set the `href` value *exactly* as shown here. 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 :marked
### Router imports ### 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`. 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. 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 .l-sub-section
:marked :marked
We cover other options in the [details below](#browser-url-styles). 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. 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. 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 .l-sub-section
:marked :marked
@ -122,13 +125,15 @@ include ../_util-fns
a configured *Router* module to our root NgModule imports. a configured *Router* module to our root NgModule imports.
:marked :marked
Next we open `app.module.ts` where we must register our routing, routing providers, and declare our two route components. 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 :marked
### Router Outlet ### Router Outlet
Given this configuration, when the browser URL for this application becomes `/heroes`, 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` 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. 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 --> <!-- Routed views go here -->
<router-outlet></router-outlet> <router-outlet></router-outlet>
:marked :marked
@ -150,7 +155,9 @@ code-example(format="", language="html").
or on its parent element. or on its parent element.
We see such bindings in the following `AppComponent` template: 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 .l-sub-section
:marked :marked
We're adding two anchor tags with `RouterLink` and `RouterLinkActive` directives. We're adding two anchor tags with `RouterLink` and `RouterLinkActive` directives.
@ -257,6 +264,7 @@ table
We gloss over everything in between. We gloss over everything in between.
The full source is available in the <live-example></live-example>. The full source is available in the <live-example></live-example>.
:marked :marked
Our client is the Hero Employment Agency. Our client is the Hero Employment Agency.
Heroes need work and The Agency finds Crises for them to solve. Heroes need work and The Agency finds Crises for them to solve.
@ -336,7 +344,7 @@ figure.image-display
figure.image-display figure.image-display
img(src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" ) img(src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" )
<a id="base-href"></a> a#base-href
:marked :marked
### Set the *&lt;base href>* ### Set the *&lt;base href>*
The Component Router uses the browser's 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, 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. 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 .l-sub-section
:marked :marked
HTML 5 style navigation is the Component Router default. 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, 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. 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 .l-sub-section
:marked :marked
@ -443,7 +452,9 @@ h4#register-providers Register routing in the root NgModule
so they will be registered within our root NgModule. so they will be registered within our root NgModule.
We also import the `appRoutingProviders` array and add it to the `providers` array. 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 :marked
Providing the router module in our root NgModule makes the Router available everywhere in our application. 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 a#shell-template
:marked :marked
The corresponding component template looks like this: 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 :marked
### *RouterOutlet*
`RouterOutlet` is a component from the router library. `RouterOutlet` is a component from the router library.
The router displays views within the bounds of the `<router-outlet>` tags. 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>`. A template may hold exactly one ***unnamed*** `<router-outlet>`.
The router supports multiple *named* outlets, a feature we'll cover in future. The router supports multiple *named* outlets, a feature we'll cover in future.
h3#router-link <i>RouterLink</i> binding a#router-link
:marked :marked
### *RouterLink* binding
Above the outlet, within the anchor tags, we see [Property Bindings](template-syntax.html#property-binding) to 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. 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. They are readily available for us to use in our template.
:marked :marked
The current state of `app.component.ts` looks like this: 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 :marked
### "Getting Started" wrap-up ### "Getting Started" wrap-up
@ -545,6 +562,7 @@ h3#router-directives <i>Router Directives</i>
.file typings.json .file typings.json
:marked :marked
Here are the files discussed in this milestone Here are the files discussed in this milestone
+makeTabs( +makeTabs(
`router/ts/app/app.component.1.ts, `router/ts/app/app.component.1.ts,
router/ts/app/app.module.1.ts, router/ts/app/app.module.1.ts,
@ -562,9 +580,10 @@ h3#router-directives <i>Router Directives</i>
crisis-list.component.ts, crisis-list.component.ts,
index.html`) index.html`)
h2#heroes-feature Milestone #2: The Heroes Feature .l-main-section#heroes-feature
.l-main-section
:marked :marked
## Milestone #2: The Heroes Feature
We've seen how to navigate using the `RouterLink` directive. We've seen how to navigate using the `RouterLink` directive.
Now we'll learn some new tricks such as how to 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. so its available to all components within our module.
Our `Heroes` module is ready for routing. 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 :marked
When we're done organizing, we have four *Hero Management* files: 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. We recommend giving each feature area its own route configuration file.
Create a new `heroes.routing.ts` in the `heroes` folder like this: 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 :marked
We use the same techniques we learned for `app.routing.ts`. 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. 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. 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 :marked
### Route definition with a parameter ### Route definition with a parameter
The route to `HeroDetailComponent` has a twist. 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 :marked
Notice the `:id` token in the path. That creates a slot in the path for a **Route Parameter**. 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. 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`. 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 :marked
### Navigate to hero detail imperatively
*We won't navigate to the detail component by clicking a link* *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. 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 We'll adjust the `HeroListComponent` to implement these tasks, beginning with its constructor
which acquires the router service and the `HeroService` by dependency injection: 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 :marked
We make a few changes to the template: 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 :marked
The template defines an `*ngFor` repeater such as [we've seen before](displaying-data.html#ngFor). 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 There's a `(click)` [EventBinding](template-syntax.html#event-binding) to the component's `onSelect` method
which we implement as follows: 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 :marked
It calls the router's **`navigate`** method with a **Link Parameters Array**. We can use this same syntax 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. 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 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. `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 :marked
The router composes the appropriate two-part destination URL from this array: 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 localhost:3000/hero/15
h3#get-route-parameter Getting the route parameter in the details view
a#get-route-parameter
:marked :marked
### Getting the route parameter in the details view
How does the target `HeroDetailComponent` learn about that `id`? How does the target `HeroDetailComponent` learn about that `id`?
Certainly not by analyzing the URL! That's the router's job. Certainly not by analyzing the URL! That's the router's job.
@ -735,14 +774,18 @@ a#hero-detail-ctor
:marked :marked
As usual, we write a constructor that asks Angular to inject services As usual, we write a constructor that asks Angular to inject services
that the component requires and reference them as private variables. 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 :marked
Later, in the `ngOnInit` method, Later, in the `ngOnInit` method,
we use the `ActivatedRoute` service to retrieve the parameters for our route. 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 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 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. 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 .l-sub-section
:marked :marked
Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent`. 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.* *Failure to do so could create a memory leak.*
We unsubscribe from our `Observable` in the `ngOnDestroy` method. 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 .l-sub-section
:marked :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. 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`. We don't need to subscribe. We don't have to unsubscribe in `ngOnDestroy`.
It's much simpler to write and read: 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 .l-sub-section
:marked :marked
**Remember:** we only get the _initial_ value of the parameters with this technique. **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. to this component multiple times in a row.
We are leaving the observable `params` strategy in place just in case. 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 :marked
### Navigating back to the list component
The `HeroDetailComponent` has a "Back" button wired to its `gotoHeroes` method that navigates imperatively The `HeroDetailComponent` has a "Back" button wired to its `gotoHeroes` method that navigates imperatively
back to the `HeroListComponent`. back to the `HeroListComponent`.
The router `navigate` method takes the same one-item *link parameters array* The router `navigate` method takes the same one-item *link parameters array*
that we can bind to a `[routerLink]` directive. that we can bind to a `[routerLink]` directive.
It holds the **path to the `HeroListComponent`**: 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 h3#merge-hero-routes Import hero module into root NgModule
:marked :marked
@ -826,7 +875,7 @@ h3#merge-hero-routes Import hero module into root NgModule
Update `app.module.ts` as follows: 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 :marked
We imported the `HeroesModule` and added it to our root NgModule `imports`. 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`. 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 :marked
### Heroes App Wrap-up ### Heroes App Wrap-up
@ -883,6 +932,7 @@ h3#merge-hero-routes Import hero module into root NgModule
<a id="heroes-app-code"></a> <a id="heroes-app-code"></a>
### The Heroes App code ### The Heroes App code
Here are the relevant files for this version of the sample application. Here are the relevant files for this version of the sample application.
+makeTabs( +makeTabs(
`router/ts/app/app.component.1.ts, `router/ts/app/app.component.1.ts,
router/ts/app/app.module.2.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, hero.service.ts,
heroes.module.ts, heroes.module.ts,
heroes.routing.ts`) heroes.routing.ts`)
:marked
<a id="crisis-center-feature"></a> .l-main-section#crisis-center-feature
.l-main-section
:marked :marked
## Milestone #3: The Crisis Center ## Milestone #3: The Crisis Center
The *Crisis Center* is a fake view at the moment. Time to make it useful. 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. 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 figure.image-display
img(src='/resources/images/devguide/router/component-tree.png' alt="Component Tree" ) img(src='/resources/images/devguide/router/component-tree.png' alt="Component Tree" )
h3#child-routing-component Child Routing Component a#child-routing-component
:marked :marked
### Child Routing Component
Add the following `crisis-center.component.ts` to the `crisis-center` folder: 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 :marked
The `CrisisCenterComponent` is much like the `AppComponent` shell. 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**. 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. 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. We *navigate* to it from the outside, via the router.
.l-sub-section .l-sub-section
:marked :marked
We *can* give it a selector. There's no harm in it. 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 &mdash; Instead of registering it with the root NgModule's providers &mdash;
which makes it visible everywhere &mdash; which makes it visible everywhere &mdash;
we register the `CrisisService` in the `CrisisCenterModule` providers array. 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 :marked
This limits the scope of the `CrisisService` to the *Crisis Center* routes. This limits the scope of the `CrisisService` to the *Crisis Center* routes.
No module outside of the *Crisis Center* can access it. No module outside of the *Crisis Center* can access it.
@ -1011,12 +1067,15 @@ h3#child-routing-component Child Routing Component
:marked :marked
### Child Route Configuration ### Child Route Configuration
The `CrisisCenterComponent` is a *Routing Component* like the `AppComponent`. The `CrisisCenterComponent` is a *Routing Component* like the `AppComponent`.
It has its own `RouterOutlet` and its own child routes. 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. 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. 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 :marked
Notice that the parent `crisis-center` route has a `children` property Notice that the parent `crisis-center` route has a `children` property
with an array of two routes. 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, `/`, To write an URL that navigates to the `CrisisDetailComponent`, we'd append the child route path, `/`,
followed by the crisis id, yielding something like: followed by the crisis id, yielding something like:
code-example(format=""). code-example.
localhost:3000/crisis-center/2 localhost:3000/crisis-center/2
:marked :marked
Here's the complete `crisis-center.routing.ts` with its imports. 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 h3#import-crisis-module Import crisis center module into the root NgModule routes
:marked :marked
As with the `Heroes` module, we must import the `Crisis Center` module into the root NgModule: 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 :marked
We also remove the initial crisis center route from our `app.routing.ts`. Our routes 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 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. 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 a#redirect
h3#redirect Redirecting routes
:marked :marked
### Redirecting routes
When the application launches, the initial URL in the browser bar is something like: When the application launches, the initial URL in the browser bar is something like:
code-example(format="").
code-example.
localhost:3000 localhost:3000
:marked :marked
That doesn't match any of our configured routes which means that our application won't display any component when it's launched. 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. 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 (`''`) The preferred solution is to add a `redirect` route that transparently translates from the initial relative URL (`''`)
to the desired default path (`/crisis-center`): 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 :marked
A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route. 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 :marked
The updated route definitions look like this: 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 .l-main-section
h2#guards Route Guards h2#guards Route Guards
@ -1153,15 +1220,16 @@ h2#guards Route Guards
Let's look at some examples. Let's look at some examples.
.l-main-section .l-main-section#lifecycle-hooks
// :marked :marked
<a id="lifecycle-hooks"></a>
## Router Lifecycle Hooks ## Router Lifecycle Hooks
TODO: Pausing activation TODO: Pausing activation
h3#can-activate-guard <i>CanActivate</i>: requiring authentication a#can-activate-guard
:marked :marked
### *CanActivate*: requiring authentication
Applications often restrict access to a feature area based on who the user is. 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 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. 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. 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. 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 :marked
Next, we add a child route to the `crisis-center.routes` with the path, `/admin`. 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 :marked
And we add a link to the `AppComponent` shell that users can click to get to this feature. 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 .l-sub-section
:marked :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. 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: 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 :marked
Next we open `crisis-center.routing.ts `, import the `AuthGuard` class, and Next we open `crisis-center.routing.ts `, import the `AuthGuard` class, and
update the admin route with a `CanActivate` guard property that references it: 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 :marked
Our admin feature is now protected by the guard, albeit protected poorly.
#### Teach *AuthGuard* to authenticate #### Teach *AuthGuard* to authenticate
Let's make our `AuthGuard` at least pretend 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. The `AuthGuard` should call an application service that can login a user and retain information about the current user.
Here's a demo `AuthService`: Here's a demo `AuthService`:
+makeExample('router/ts/app/auth.service.ts', '', 'app/auth.service.ts')(format=".")
+makeExcerpt('app/auth.service.ts')
:marked :marked
Although it doesn't actually log in, it has what we need for this discussion. 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. 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. The `redirectUrl` property will store our attempted URL so we can navigate to it after authenticating.
Let's revise our `AuthGuard` to call it. 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 :marked
Notice that we *inject* the `AuthService` and the `Router` in the constructor. 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. 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. We discard the changes if the user presses he *Cancel* button.
Both buttons navigate back to the crisis list after save or cancel. 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 :marked
What if the user tries to navigate away without saving or canceling? 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. 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 *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`). 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 :marked
We create a `Guard` that will check for the presence of a `canDeactivate` function in our component, in this 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. 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. 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 :marked
Alternatively, We could make a component-specific `CanDeactivate` guard for our `CrisisDetailComponent`. The `canDeactivate` method provides us 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 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 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. 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 :marked
Looking back at our `CrisisDetailComponent`, we have implemented our confirmation workflow for unsaved changes. 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 :marked
Notice that the `canDeactivate` method *can* return synchronously; Notice that the `canDeactivate` method *can* return synchronously;
it returns `true` immediately if there is no crisis or there are no pending changes. 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 :marked
We add the `Guard` to our crisis detail route in `crisis-center.routing.ts` using the `canDeactivate` array. 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 :marked
We also need to add the `Guard` to our main `appRoutingProviders` so the `Router` can inject it during the navigation process. 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 :marked
Now we have given our user a safeguard against unsaved changes. 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. 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 :marked
We'll take the relevant parts of the `ngOnInit` lifecycle hook in our `CrisisDetailComponent` and moved them into our `CrisisDetailResolve` guard. 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. 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 :marked
We'll add the `CrisisDetailResolve` service to our crisis center module's `providers`, so its available to the `Router` during the navigation process. 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 :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`. 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 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. 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 :marked
**Two critical points** **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#optional-route-parameters
.l-main-section
:marked :marked
## Milestone #4: Route Parameters ## Milestone #4: Route Parameters
@ -1500,18 +1587,24 @@ figure.image-display
<a id="route-parameters-object"></a> <a id="route-parameters-object"></a>
### Route parameter ### Route parameter
When navigating to the `HeroDetailComponent` we specified the `id` of the hero-to-edit in the 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). *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 :marked
The router embedded the `id` value in the navigation URL because we had defined it 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`: 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 :marked
When the user clicks the back button, the `HeroDetailComponent` constructs another *link parameters array* When the user clicks the back button, the `HeroDetailComponent` constructs another *link parameters array*
which it uses to navigate back to the `HeroListComponent`. 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 :marked
This array lacks a route parameter because we had no reason to send information to the `HeroListComponent`. 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 do that with an object that contains our optional `id` parameter.
We also defined a junk parameter (`foo`) that the `HeroListComponent` should ignore. We also defined a junk parameter (`foo`) that the `HeroListComponent` should ignore.
Here's the revised navigation statement: 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 :marked
The application still works. Clicking "back" returns to the hero list view. 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. When running in plunker, pop out the preview window by clicking the blue 'X' button in the upper right corner.
:marked :marked
It should look something like this, depending on where you run it: 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 localhost:3000/heroes;id=15;foo=foo
:marked :marked
The `id` value appears in the URL as (`;id=15;foo=foo`), not in the URL path. 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. The path for the "Heroes" route doesn't have an `:id` token.
:marked
The optional route parameters are not separated by "?" and "&". The optional route parameters are not separated by "?" and "&".
They are **separated by semicolons (;)** They are **separated by semicolons (;)**
This is *matrix URL* notation &mdash; something we may not have seen before. This is *matrix URL* notation &mdash; something we may not have seen before.
.l-sub-section .l-sub-section
:marked :marked
*Matrix URL* notation is an idea first floated *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`. 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; 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 :marked
Then we use the `ActivatedRoute` to access the `params` _Observable_ so we can subscribe Then we use the `ActivatedRoute` to access the `params` _Observable_ so we can subscribe
and extract the `id` parameter as the `selectedId`: 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 .l-sub-section
:marked :marked
All route/query parameters are strings. 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. The (+) in front of the `params['id']` expression is a JavaScript trick to convert the string to an integer.
:marked :marked
We add an `isSelected` method that returns true when a hero's id matches the selected id. 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 :marked
Finally, we update our template with a [Class Binding](template-syntax.html#class-binding) to that `isSelected` method. 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`. 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: 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 :marked
When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected: When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected:
figure.image-display figure.image-display
@ -1599,11 +1705,11 @@ figure.image-display
:marked :marked
The optional `foo` route parameter is harmless and continues to be ignored. The optional `foo` route parameter is harmless and continues to be ignored.
<a id="query-parameters"></a> a#query-parameters
<a id="fragment"></a> a#fragment
:marked :marked
### Query Parameters and Fragments ### Query Parameters and Fragments
:marked
In our [route parameters](#optional-route-parameters) example, we only dealt with parameters specific to 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 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. 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 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. 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 :marked
We can also **preserve** query parameters and fragments across navigations without having to re-provide them 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 provide the `preserveQueryParams` and `preserveFragment` to pass along the current query parameters
and fragment to the next route. 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 :marked
Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our
query parameters and fragment. 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 :marked
*Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components. *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`. 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 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. 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 :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`. 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 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`. `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 :marked
If our initial redirect went to `/heroes` instead of going to `/crisis-center`, the `CrisisCenterModule` would not be loaded until the user 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. 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> <a id="final-app"></a>
.l-main-section .l-main-section
@ -1739,10 +1847,10 @@ figure.image-display
The appendix material isn't essential. Continued reading is for the curious. The appendix material isn't essential. Continued reading is for the curious.
.l-main-section .l-main-section#link-parameters-array
<a id="link-parameters-array"></a>
:marked :marked
## Appendix: Link Parameters Array ## Appendix: Link Parameters Array
We've mentioned the *Link Parameters Array* several times. We've used it several times. 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: 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 * required and optional route parameters that go into the route URL
We can bind the `RouterLink` directive to such an array like this: 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 :marked
We've written a two element array when specifying a route parameter like this 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 :marked
We can provide optional route parameters in an object like this: 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 :marked
These three examples cover our needs for an app with one level routing. 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. 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. 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 :marked
Let's parse it out. Let's parse it out.
* The first item in the array identifies the parent route ('/crisis-center'). * 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`) * We add `id` of the *Dragon Crisis* as the second item in the array (`1`)
It looks like this! It looks like this!
+makeExample('router/ts/app/app.component.3.ts', 'Dragon-anchor')(format=".")
+makeExcerpt('app/app.component.3.ts', 'Dragon-anchor', '')
:marked :marked
If we wanted to, we could redefine our `AppComponent` template with *Crisis Center* routes exclusively: 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 :marked
In sum, we can write applications with one, two or more levels of routing. 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 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. any legal sequence of route paths, (required) router parameters and (optional) route parameter objects.
<a id="onInit"></a> .l-main-section#onInit
.l-main-section
:marked :marked
## Appendix: Why use an *ngOnInit* method ## 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 The preferred way to configure the strategy is to add a
[&lt;base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag [&lt;base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag
in the `<head>` of the `index.html`. in the `<head>` of the `index.html`.
+makeExample('router/ts/index.1.html','base-href')(format=".")
+makeExcerpt('index.1.html', 'base-href', '')
:marked :marked
Without that tag, the browser may not be able to load resources Without that tag, the browser may not be able to load resources
(images, css, scripts) when "deep linking" into the app. (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 We can go old-school with the `HashLocationStrategy` by
providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot` providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot`
in our root NgModule. 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)', '')