docs(router): improvements + arch typo fix

closes #592
This commit is contained in:
Ward Bell 2015-12-19 14:16:27 -08:00
parent 7e86e9411b
commit e14f950a7c
3 changed files with 148 additions and 139 deletions

View File

@ -4,11 +4,11 @@
import {Component, OnInit} from 'angular2/core';
import {Crisis, CrisisService} from './crisis.service';
import {RouteParams, Router} from 'angular2/router';
// #docregion ngCanDeactivate
// #docregion routerCanDeactivate
import {CanDeactivate, ComponentInstruction} from 'angular2/router';
import {DialogService} from '../dialog.service';
// #enddocregion ngCanDeactivate
// #enddocregion routerCanDeactivate
@Component({
// #docregion template
@ -28,13 +28,13 @@ import {DialogService} from '../dialog.service';
// #enddocregion template
styles: ['input {width: 20em}']
})
// #docregion ngCanDeactivate, cancel-save
// #docregion routerCanDeactivate, cancel-save
export class CrisisDetailComponent implements OnInit, CanDeactivate {
public crisis: Crisis;
public editName: string;
// #enddocregion ngCanDeactivate, cancel-save
// #enddocregion routerCanDeactivate, cancel-save
constructor(
private _service: CrisisService,
private _router: Router,
@ -87,7 +87,7 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate {
this._router.navigate(route);
}
// #enddocregion gotoCrises
// #docregion ngCanDeactivate, cancel-save
// #docregion routerCanDeactivate, cancel-save
}
// #enddocregion ngCanDeactivate, cancel-save
// #enddocregion routerCanDeactivate, cancel-save
// #enddocregion

View File

@ -403,7 +403,7 @@ figure
Here's an example of a service class that logs to the browser console
+makeExample('architecture/ts/app/logger.service.ts', 'class', 'app/logger.service.ts (class only)')(format=".")
:marked
Here's a `HeroServce` that fetches heroes and returns them in a resolved [promise](http://www.html5rocks.com/en/tutorials/es6/promises/).
Here's a `HeroService` that fetches heroes and returns them in a resolved [promise](http://www.html5rocks.com/en/tutorials/es6/promises/).
The `HeroService` depends on the `LoggerService` and another `BackendService` that handles the server communication grunt work.
+makeExample('architecture/ts/app/hero.service.ts', 'class', 'app/hero.service.ts (class only)')(format=".")
:marked

View File

@ -53,9 +53,8 @@ include ../../../../_includes/_util-fns
:marked
Now we know how the router gets its configuration.
When the browser URL for this application becomes `/heroes`,
the router finds the `RouteDefintion` named *Heroes* and then knows to display the `HeroListComponent`.
Display it where? It will display in a **`RouterOutlet`** that we've placed in the host view's HTML.
the router matches that URL to the `RouteDefintion` named *Heroes* and displays the `HeroListComponent`
in a **`RouterOutlet`** that we've placed in the host view's HTML.
code-example(format="", language="html").
<!-- Routed views go here -->
<router-outlet></router-outlet>
@ -65,8 +64,8 @@ code-example(format="", language="html").
But most of the time we navigate as a result of some user action such as the click of
an anchor tag.
In an anchor tag we bind a **`RouterLink`** Directive to a template expression that
returns an **array of route link parameters**. The router ultimately resolves that array
We add a **`RouterLink`** directive to the anchor tag and bind it to a template expression that
returns an array of route link parameters (the **link parameters array**). The router ultimately resolves that array
into a URL and a component view.
We see such bindings in the following `AppComponent` template:
@ -77,7 +76,8 @@ code-example(format="", language="html").
We bind each `RouterLink` to an array containing the string name of a route definition.
'CrisisCenter' and 'Heroes' are the names of the `Routes` we configured above.
We'll learn to write more complex link expressions ... and why they are arrays ... later in the chapter.
We'll learn to write more complex link expressions — and why they are arrays —
[later](#link-parameter-array) in the chapter.
:marked
### Let's summarize
@ -126,31 +126,38 @@ table
An array that the router inteprets into a routing instruction.
We can bind a <code>RouterLink</code> to that array or pass the array as an argument to
the <code>Router.navigate</code> method.
tr
td <i>Routing Component</i></code>
td.
An Angular component with an attached router.
:marked
We'll learn many more details in this chapter which covers
* [configuring a router](#route-config)
* the [link parameter arrays](#link-parameters-array) that propel router navigation
* navigating when the user clicks a data-bound ['RouterLink'](#router-link)
* navigating when the user clicks a data-bound [RouterLink](#router-link)
* navigating under [program control](#navigate)
* passing information in [route parameters](#route-parameter)
* creating a [child router](#child-router) with its own routes
* setting a [default route](#default)
* pausing, confirming and/or canceling a navigation with the the 'CanDeactivate' [lifecycle hook](#lifecycle-hooks)
* pausing, confirming and/or canceling a navigation with the the `routerCanDeactivate` [router lifecycle hook](#lifecycle-hooks)
We will proceed in phases marked by milestones.
Our first milestone is the ability to navigate between between two placeholder views.
We proceed in phases marked by milestones.
Our first milestone is the ability to navigate between two placeholder views.
At our last milestone, we'll have a modular, multi-view design with child routes.
We assume that you're already comfortable with the basic Angular 2 concepts and tools
We assume that you're already comfortable with the basic Angular 2 tools and concepts
we introduced in the [QuickStart](../quickstart.html) and
the [Tour of Heroes](../tutorial/) tutorial.
.l-sub-section
:marked
While we make incremental progress on a sample application, this chapter is not a tutorial.
We discuss code and design decisions pertinent to routing and application design.
We gloss over everything in between.
The full source is available in the [live example](/resources/live-examples/router/ts/plnkr.html).
:marked
While there is a progression, this chapter is not a tutorial.
We discuss code and design decisions pertinent to routing and application design.
We gloss over everything else.
The full source is available in the [live example](/resources/live-examples/router/ts/plnkr.html).
.l-main-section
:marked
@ -189,22 +196,24 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/router/crisis-center-detail.png' alt="Crisis Center Detail")
:marked
This is a bit different then the "Heroes Detail". We have two buttons, "Save" and "Cancel".
If we make a change and click "Save", we return to the *Crisis Center* and see our changes
reflected in the list. If we make a change and click "Cancel",
we return to the *Crisis Center* but this time our changes were discarded.
This is a bit different from the *Hero Detail*. *Hero Detail* saves the changes as we type.
In *Crisis Detail* our changes are temporary until we either save or discard them
with by pressing the "Save" or "Cancel" buttons.
Both buttons navigate back to the *Crisis Center* and its list of crises.
Now we click a crisis, make a change, and ***do not click either button***.
We click the browser back button instead. Up pops a modal dialog box.
Suppose we click a crisis, make a change, but ***do not click either button***.
Maye we click the browser back button instead. Maybe we click the "Heroes" link.
Do either. Up pops a dialog box.
figure.image-display
img(src='/resources/images/devguide/router/confirm-dialog.png' alt="Confirm Dialog" width="300")
:marked
We can say "OK" and lose our changes or click "Cancel" and continue editing.
The router supports a `CanDeactivate` lifecycle" method that gives us a chance to clean-up
The router supports a `routerCanDeactivate` lifecycle hook that gives us a chance to clean-up
or ask the user's permission before navigating away from the current view.
Let's see a quick demonstration of the workflow in action.
Here we see an entire user session that touches all of these features.
<a id="full-app-demo"></a>
figure.image-display
img(src='/resources/images/devguide/router/router-anim.gif' alt="App in action" )
@ -218,7 +227,7 @@ figure.image-display
* navigating to a component (*Heroes* link to "Heroes List")
* including a route parameter (passing the Hero `id` while routing to the "Hero Detail")
* child routes (the *Crisis Center* has its own routes)
* the `CanDeactivate` lifecycle method (ask before discarding changes)
* the `routerCanDeactivate` lifecycle hook (ask permission to discard unsaved changes)
<a id="getting-started"></a>
.l-main-section
@ -230,7 +239,7 @@ figure.image-display
img(src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" )
:marked
### Load the Component Router library
The Component Router is not part of the Angular 2 core. It is its own library.
The Component Router is not part of the Angular 2 core. It is in its own library.
The router is an optional service and you might prefer a different router someday.
The Component Router library is part of the Angular npm bundle.
@ -282,21 +291,21 @@ figure.image-display
:marked
We import our root `AppComponent` and Angular's `bootstrap` function as expected.
We also import `ROUTER_PROVIDERS` from Dependency Injection.
The router is a service implemented by a collection of providers, most of which are identified in the
We also import `ROUTER_PROVIDERS` from the router library.
The router is a service implemented by a collection of *Dependency Injection* providers, most of which are identified in the
`ROUTER_PROVIDERS` array.
As usual, we're booting Angular with `AppComponent` as our app's root component and
registering providers in an array in the second parameter of the `bootstrap` function.
Providing the router providers at the root makes the router available everywhere in our application.
We're booting Angular with `AppComponent` as our app's root component and
registering providers, as we often do, in the providers array in the second parameter of the `bootstrap` function.
Providing the router providers at the root makes the Component Router available everywhere in our application.
.l-sub-section
:marked
Learn about providers, the `provide` function, and injected services in the
[Dependency Injection chapter](dependency-injection.html).
:marked
### The *AppComponent* shell
The root `AppComponent` is the shell of our application. It has title at the top, a navigation bar with two links,
and a "router outlet" below where the router swaps views on and off the page. Here's what we mean:
The root `AppComponent` is the application shell. It has title at the top, a navigation bar with two links,
and a *Router Outlet* at the bottom where the router swaps views on and off the page. Here's what we mean:
figure.image-display
img(src='/resources/images/devguide/router/shell-and-outlet.png' alt="Shell" width="300" )
:marked
@ -316,7 +325,7 @@ figure.image-display
: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.
the `RouterLink` directive that look like `[routerLink]="[...]"`. We imported `RouterLink` from the router library.
The template expression to the right of the equals (=) returns a *link parameters array*.
@ -356,7 +365,7 @@ figure.image-display
1. the browser URL changes
2. we tell the router to go to a named route
Translating these two definitions into English, we might say:
In plain English, we might say of the first route:
1. *When the browser's location URL changes to **match the path** segment `/crisis-center`, create or retrieve an instance of
the `CrisisCenterComponent` and display its view.*
@ -377,7 +386,7 @@ figure.image-display
* set the router to compose "HTML 5" browser URLs.
The rest of the starter app is mundane, with little interest from a router perspective.
Here are the details for readers inclined to build the sample through this milestone.
Here are the details for readers inclined to build the sample through to this milestone.
Our starter app's structure looks like this:
.filetree
@ -414,25 +423,25 @@ figure.image-display
We've seen how to navigate using the `RouterLink` directive.
Now we'll learn some new tricks such as how to
* organize our app into "feature areas"
* organize our app into *feature areas*
* navigate imperatively from one component to another
* pass information along in route parameters (`RouteParams`)
To demonstrate all of this we'll build out the *Heroes* feature.
To demonstrate, we'll build out the *Heroes* feature.
### The Heroes "feature area"
A typical application has multiple "feature areas", each an island of functionality
dedicated to an area of interest with its own workflow(s).
A typical application has multiple *feature areas*, each an island of functionality
with its own workflow(s), dedicated to a particular business purpose.
We could continue to add files to the `app/` folder.
That's unrealistic and ultimately not maintainable.
We think it's best if each feature area is in its own folder.
We think it's better to put each feature area in its own folder.
Our first step is to **create a separate `app/heroes/` folder**.
Then we'll add Hero management feature files.
and add *Hero Management* feature files there.
We won't be creative about this. Our example is pretty much a
We won't be creative about it. Our example is pretty much a
copy of the code and capabilities in the "[Tutorial: Tour of Heroes](../tutorial/index.html)".
Here's how the user will experience this version of the app
@ -447,9 +456,9 @@ figure.image-display
We create a new `hero-list.component.ts` in the `app/heroes/`
folder and copy over the contents of the final `heroes.component.ts` from the tutorial.
We also copy the `hero-detail.component.ts` and the `hero.service.ts` files
into the `heroes/` folder while we're at it.
into the `heroes/` folder.
When were done organizing, we have three "Hero" files:
When were done organizing, we have three *Hero Management* files:
.filetree
.file app/heroes
@ -458,8 +467,8 @@ figure.image-display
.file hero-list.component.ts
.file hero.service.ts
:marked
Here as in the tutorial, we'll provide the `HeroService` during bootstrapping
so that is available anywhere in the app (see `boot.ts`) .
We'll provide the `HeroService` during bootstrapping
so that is available everywhere in the app (see `boot.ts`) .
Now it's time for some surgery to bring these files and the rest of the app
into alignment with our application router.
@ -488,18 +497,18 @@ figure.image-display
While we moved `hero-list.component.ts` to a new location in the `app/heroes/` folder, that only affects the `import` statement;
it doesn't affect its route definition.
We added a new route definition for the `HeroDetailComponent` ... and this definition has a twist.
We added a new route definition for the `HeroDetailComponent` &mdash; and this definition has a twist.
+makeExample('router/ts/app/app.component.2.ts','hero-detail-route')(format=".")
:marked
Notice the `:id` token in the 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.
If we tell the router to navigate to the detail component and display "Magenta", we expect her `id` (15) to appear in the
If we tell the router to navigate to the detail component and display "Magenta", we expect hero `id` (15) to appear in the
browser URL like this:
code-example(format="." language="bash").
localhost:3000/hero/15
:marked
If someone enters that URL into the browser address bar, the router should recognize the
If a user enters that URL into the browser address bar, the router should recognize the
pattern and go to the same "Magenta" detail view.
<a id="navigate"></id>
@ -511,22 +520,21 @@ code-example(format="." language="bash").
Instead, we'll *detect* when the user selects a hero from the list and *command* the router
to present the hero detail view of the selected hero.
We'll adjust the `HeroListComponent` to implement these tasks beginning with its template:
+makeExample('router/ts/app/heroes/hero-list.component.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 `select` method.
The `select` method will call the router service which we acquire by dependency injection
(along with the `HeroService` that gives us heroes to show):
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.ts','ctor')(format=".")
:marked
Here's the `select` method:
We make a few changes to the template:
+makeExample('router/ts/app/heroes/hero-list.component.ts','template')(format=".")
: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.ts','select')(format=".")
:marked
It calls the router's **`navigate`** method with a **Link Parameters Array**.
This one is similar to the *link parameters array* we met [earlier](#shell-template) in an anchor tag,
binding to the `RouterLink` directive, only this time we're seeing it in code rather than in HTML.
This array is similar to the *link parameters array* we met [earlier](#shell-template) in an anchor tag while
binding to the `RouterLink` directive. This time we see it in code rather than in HTML.
<a id="route-parameter"></id>
### Setting the route parameter
@ -537,14 +545,14 @@ code-example(format="." language="bash").
`id` of the selected hero.
+makeExample('router/ts/app/heroes/hero-list.component.ts','link-parameters-array')(format=".")
:marked
The router composes the appropriate two-part destination URL:
The router composes the appropriate two-part destination URL from this array:
code-example(format="." language="bash").
localhost:3000/hero/15
:marked
### Getting the route parameter
<a id="hero-detail-ctor"></a>
How does the target `HeroDetailComponent` get that `id`?
How does the target `HeroDetailComponent` learn about that `id`?
Certainly not by analyzing the URL! That's the router's job.
The router extracts the route parameter (`id:15`) from the URL and supplies it to
@ -571,8 +579,9 @@ code-example(format="." language="bash").
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*, holding
the **name of the `HeroListComponent` route**, that we used in the `[routerLink]` directive binding.
The router `navigate` method takes the same one-item *link parameters array*
that we wrote for the `[routerLink]` directive binding.
It holds the **name of the `HeroListComponent` route**:
+makeExample('router/ts/app/heroes/hero-detail.component.ts','gotoHeroes')(format=".")
:marked
### Heroes App Wrap-up
@ -580,7 +589,7 @@ code-example(format="." language="bash").
We've reached the second milestone in our router education.
We've learned how to
* organize our app into "feature areas"
* organize our app into *feature areas*
* navigate imperatively from one component to another
* pass information along in route parameters (`RouteParams`)
@ -638,10 +647,8 @@ code-example(format="." language="bash").
Voilà, instant feature module!
Of course this is only a sample application.
There's no point to this exercise unless we can learn something new.
We do have new points to make:
There's no point to this exercise unless we can learn something.
We do have new ideas and techniques in mind:
* The application should navigate to the *Crisis Center* by default.
@ -665,8 +672,8 @@ code-example(format="." language="bash").
We'll fix all of these problems and add the new routing features to *Crisis Center*.
The most important fix, from a router perspective, is the introduction of a **child *Routing Component***
with its **child router**
The most significant fix is the introduction of a **child *Routing Component***
and its **child router**
We'll leave *Heroes* in its less-than-perfect state to
serve as a contrast with what we hope is a superior *Crisis Center*.
@ -678,8 +685,8 @@ code-example(format="." language="bash").
The `CrisisCenter` and `Heroes` components are children of the root `AppComponent`.
Unfortunately, they and their related files are physically commingled in the same folder with the `AppComponent`.
We'd prefer to separate them in their own "feature areas" so they can operate and evolve independently.
Someday we might re-use one or the other of them in a different application.
We'd prefer to separate them in their own *feature areas* so they can operate and evolve independently.
Someday we might re-use one or the other in a different application.
Someday we might load one of them dynamically only when the user chose to enter its workflow.
Some might call it [yagni](http://martinfowler.com/bliki/Yagni.html) to even think about such things.
@ -718,13 +725,12 @@ code-example(format="." language="bash").
just as the `AppComponent` is a shell to manage the high-level workflow.
`AppComponent` has a `@RouteConfig` decorator that defines the top-level routes.
`CrisisCenterComponent` has a `@RouteConfig` decorator that defines *Crisis Center* routes.
The two sets of routes *do not overlap*.
`CrisisCenterComponent` has a `@RouteConfig` decorator that defines *Crisis Center* child routes.
`CrisisCenterComponent` template is dead simple &mdash; simpler even than the `AppComponent` template.
It has no content, no links, just a `<router-outlet>` for the *Crisis Center* views.
The `CrisisCenterComponent` template is dead simple &mdash; simpler even than the `AppComponent` template.
It has no content, no links, just a `<router-outlet>` for the *Crisis Center* child views.
It has no selector either. It doesn't need one. We don't *embed* this component in a parent template. We navigate to it
It has no selector either. It doesn't need one. We don't *embed* this component in a parent template. We *navigate* to it
from the outside, via a parent router (more on that soon).
### Service isolation
@ -742,7 +748,7 @@ code-example(format="." language="bash").
The `@RouteConfig` decorator that adorns the `CrisisCenterComponent` class defines routes in the same way
that we did earlier.
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'route-config', 'app/crisis-center/crisis-center.component.ts (routes only)' )
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'route-config', 'app/crisis-center/crisis-center.component.ts (routes only)' )(format=".")
:marked
There are three *Crisis Center* routes, two of them with an `id` parameter.
They refer to components we haven't talked about yet but whose purpose we
@ -751,12 +757,11 @@ code-example(format="." language="bash").
We cannot tell by looking at the `CrisisCenterComponent` that it is a child component
of an application. We can't tell that its routes are child routes.
That's entirely deliberate. The *Crisis Center* shouldn't know that it is the child of anything.
It might be the root of its own application. It might be repurposed in a different application.
The *Crisis Center* can be indifferent.
That's intentional. The *Crisis Center* shouldn't know that it is the child of anything.
It might be the top level component of its own application. It might be repurposed in a different application.
The *Crisis Center* itself is indifferent to these possibilities.
*We know* that it is child component in our application because we re-configured the
routes of the top-level `AppComponent` to make it so.
*We* make it a child component of our application by reconfiguring the routes of the top level `AppComponent`.
:marked
### Parent Route Configuration
Here is is the revised route configuration for the parent `AppComponent`:
@ -768,11 +773,11 @@ code-example(format="." language="bash").
:marked
Notice that the **path ends with a slash and three trailing periods (`/...`)**.
That means this is an incomplete route (AKA a ***non-terminal route***). The finished route will include the
contribution of a **child router**, the router attached to the designated component which, perforce, must be a *Routing Component*.
That means this is an incomplete route (AKA a ***non-terminal route***). The finished route will be some combination of
the parent `/crisis-center/` route and a route from the **child router** that belongs to the designated component.
All is well.
As we know, the route's component is the `CrisisCenterComponent` with its own router and routes.
The parent route's designated component is the `CrisisCenterComponent` which is a *Routing Component* with its own router and routes.
<a id="default"></a>
### Default route
@ -783,57 +788,60 @@ code-example(format="." language="bash").
### Routing to the Child
We've set the default route to go to the `CrisisCenterComponent`. We learned the this default route is incomplete.
The final route is a combination of the default route's `/crisis-center/` path fragment and one of the child `CrisisCenterComponent`
router's *three* routes. Which one?
We've set the top level default route to go to the `CrisisCenterComponent`.
The final route will be a combination of `/crisis-center/`
and one of the child `CrisisCenterComponent` router's *three* routes. Which one?
It could be any of three. In the absence of additional information, the router can't decide and must throw an error.
Our sample application didn't fail. We must have done something.
It could be any of the three. In the absence of additional information, the router can't decide and must throw an error.
We've tried the sample application and it didn't fail. We must have done something.
Scroll to the end of the `CrisisCenterComponent`s first route.
Scroll to the end of the child `CrisisCenterComponent`s first route.
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'default-route', 'app/crisis-center/crisis-center.component.ts (default route)')(format=".")
:marked
There is `useAsDefault: true` again. That tells the router to compose the final URL using the default child route.
The result is:
There is `useAsDefault: true` again. That tells the router to compose the final URL using the path from the default child route.
Concatenate the base URL with `/crisis-center/` and `/`, remove extraneous slashes, and we get:
code-example(format="").
localhost:3000//crisis-center/
.l-main-section
:marked
### Handling Unsaved Changes
Back in the "Heroes" workflow, every change to a Hero is accepted immediately without any validation.
Back in the "Heroes" workflow, the app accepts every change to a hero immediately without hesitation or validation.
In the real world, we might have to accumulate the users changes.
We might have to validate across fields. We might have to validate on the server.
We might have to hold changes in a pending state until the user confirms them all at once or
cancels and reverts.
We might have to hold changes in a pending state until the user confirms them *as a group* or
cancels and reverts all changes.
What do we do about unapproved, unsaved changes when the user navigates away?
We'd like to pause and let the user decide what to do. Perhaps we'll cancel the
navigation, stay put, and make more changes.
We need the router's cooperation to pull this off. We need lifecycle hooks.
We need the router's cooperation to pull this off. We need router lifecycle hooks.
<a id="lifecycle-hooks"></a>
### Router Lifecycle Hooks
Angular components have their own [lifecycle hooks](lifecycle-hooks.html). Angular calls the methods of the
Angular components have [lifecycle hooks](lifecycle-hooks.html). For example, Angular calls the hook methods of the
[OnInit](../api/core/OnInit-interface.html) and [OnDestroy]((../api/core/OnDestroy-interface.html)
interfaces when it creates and destroys components.
The router calls similar hook methods,
[canActivate](../api/router/CanActivate-var.html) and [canDeactivate](../api/router/CanDeactivate-interface.html),
when it is *about* to navigate to a component and when it is *about* to navigate away.
[routerCanActivate](../api/router/CanActivate-var.html) and [routerCanDeactivate](../api/router/CanDeactivate-interface.html),
before it navigates *to* a component or *away* from a component.
If a *`can...`* method returns `true`, the navigation proceeds. If it returns `false`, the
router cancels the navigation and stays on the current view.
There is a important difference between the router lifecycle hooks and the component hooks. The component hooks are synchronous.
The component hooks are synchronous and they can't stop creation or stop destruction!
That won't do for view navigation.
The router lifecycle hooks *supplement* the component lifecycle hooks.
We still need the component hooks but the router hooks do what the component hooks cannot.
For example, the component hooks can't stop component creation or destruction.
Because they are synchronous, they can't pause view navigation to wait for an asynchronous process to finish.
Imagine we have unsaved changes. The user starts to navigate away.
We can't lose the users changes. So we try to save those changes to the server.
If the save fails for any reason (perhaps the data are invalid), what do we do?
We shouldn't lose the user's changes; that would be a terrible experience. So we try to save those changes to the server.
If the save fails for any reason (perhaps the data are ruled invalid), what do we do?
If we let the user move to the next screen, we have lost the context of the error.
We can't block while waiting for the server &mdash; that's not possible in a browser.
@ -841,54 +849,55 @@ code-example(format="").
We need to stop the navigation while we wait, asynchronously, for the server
to return with its answer.
Fortunately, the router hook methods can be asynchronous and support promised.
The router hook methods can pause asynchronously, return promises, and cancel navigation if necessary.
### Cancel and Save
Our sample application doesn't talk to a server.
We can demonstrate an asynchronous router hook with a simulation.
Our sample application doesn't talk to a server.
Fortunately, we have another way to demonstrate an asynchronous router hook.
Users update crisis information in the `CrisisDetailComponent`.
Unlike the `HeroDetailComponent`, user changes do not update the
crisis entity until the user presses the *Save* button.
Unlike the `HeroDetailComponent`, the user changes do not update the
crisis entity immediately. We update the entity when the user presses the *Save* button.
We discard the changes if the user presses he *Cancel* button.
Alternatively, the user can press the *Cancel* button to discard the changes.
Both buttons navigate back to the crisis list after saving or reverting.
Both buttons navigate back to the crisis list after save or cancel.
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'cancel-save', 'crisis-detail.component.ts (excerpt)')(format=".")
:marked
But what if the user attempts to navigate away before 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.
Both actions trigger a navigation.
Should the app save or revert automatically?
Should the app save or cancel automatically?
We'll do neither. Instead we'll ask the user to make that choice ...
in a confirmation dialog service that *waits asynchronously for the user's
We'll do neither. Instead we'll ask the user to make that choice explicitly
in a confirmation dialog box that *waits asynchronously for the user's
answer*.
.l-sub-section
:marked
Waiting for the user's answer could be handled with synchronous blocking code.
But that the app will be more responsive ... and can do other work ...
if we wait for the user's answer asynchronous. Waiting for asynchronously for the user
is like waiting asynchronously for the server.
We could wait for the user's answer with synchronous, blocking code.
Our app will be more responsive ... and can do other work ...
by waiting for the user's answer asynchronously. Waiting for the user asynchronously
is like waiting for the server asynchronously.
:marked
The dialog service returns a [promise](http://www.html5rocks.com/en/tutorials/es6/promises/).
The promise *resolves* when the user eventually decides
to discard changes (`true`) or stay in the crisis editor (`false`).
to discard changes and navigate away (`true`) or keep the pending changes and stay in the crisis editor (`false`).
<a id="canDeactivate"></a>
<a id="CanDeactivate"></a>
<a id="routerCanDeactivate"></a>
:marked
We execute the dialog inside the router's `routerCanDeactivate` lifecycle hook method.
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'canDeactivate', 'crisis-detail.component.ts (excerpt)')
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'routerCanDeactivate', 'crisis-detail.component.ts (excerpt)')
:marked
Notice that the `routerCanDeactivate` method *can* return synchronously.
Notice that the `routerCanDeactivate` method *can* return synchronously;
it returns `true` immediately if there are no pending changes.
But it can also return a promise and the router will wait for that promise
to resolve before navigating away or staying put.
**Two critical points**
1. The method is optional. We don't inherit from a base class. We simply implement it or not.
1. The router hook is optional. We don't inherit from a base class. We simply implement the method or not.
1. We rely on the router to call this hook. We don't worry about all the ways that the user
1. We rely on the router to call the hook. We don't worry about all the ways that the user
could navigate away. That's the router's job.
We simply write this method and let the router take it from there.
@ -896,7 +905,7 @@ code-example(format="").
.l-main-section
:marked
## Wrap Up
As we end our chapter together, we take a parting look at
As we end our chapter, we take a parting look at
the entire application.
We can always try the [live example](../resources/live-examples/router/ts/plnkr.html) and download the source code from there.