docs(toh-5): Upgraded tutorial to new router

This commit is contained in:
Brandon Roberts 2016-06-19 00:20:38 -04:00 committed by Naomi Black
parent 2eae445504
commit f3189546a6
15 changed files with 364 additions and 267 deletions

View File

@ -54,7 +54,7 @@ nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.router-link-active {
nav a.active {
color: #039be5;
}

View File

@ -8,7 +8,7 @@ import { HeroesComponent } from './heroes.component';
// #enddocregion
// For testing only
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { ROUTER_DIRECTIVES } from '@angular/router';
// #docregion
@Component({
@ -20,7 +20,6 @@ import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/route
directives: [HeroesComponent],
providers: [
// #enddocregion
ROUTER_PROVIDERS,
// #docregion
HeroService
]

View File

@ -2,38 +2,27 @@
// #docregion
import { Component } from '@angular/core';
// #docregion import-router
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { ROUTER_DIRECTIVES } from '@angular/router';
// #enddocregion import-router
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
@Component({
selector: 'my-app',
// #docregion template
template: `
<h1>{{title}}</h1>
<a [routerLink]="['Heroes']">Heroes</a>
<a [routerLink]="['/heroes']">Heroes</a>
<router-outlet></router-outlet>
`,
// #enddocregion template
// #docregion directives-and-providers
directives: [ROUTER_DIRECTIVES],
providers: [
ROUTER_PROVIDERS,
HeroService
]
// #enddocregion directives-and-providers
})
// #docregion route-config
@RouteConfig([
{
path: '/heroes',
name: 'Heroes',
component: HeroesComponent
}
])
// #enddocregion route-config
export class AppComponent {
title = 'Tour of Heroes';
}

View File

@ -0,0 +1,33 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
import { HeroService } from './hero.service';
@Component({
selector: 'my-app',
// #docregion template
template: `
<h1>{{title}}</h1>
<nav>
// #docregion router-link-active
<a [routerLink]="['/dashboard']" routerLinkActive="active">Dashboard</a>
<a [routerLink]="['/heroes']" routerLinkActive="active">Heroes</a>
// #enddocregion router-link-active
</nav>
<router-outlet></router-outlet>
`,
// #enddocregion template
// #docregion style-urls
styleUrls: ['app/app.component.css'],
// #enddocregion style-urls
directives: [ROUTER_DIRECTIVES],
providers: [
HeroService
]
})
export class AppComponent {
title = 'Tour of Heroes';
}
// #enddocregion

View File

@ -24,6 +24,6 @@ nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.router-link-active {
nav a.active {
color: #039be5;
}

View File

@ -1,13 +1,8 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { ROUTER_DIRECTIVES } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
// #docregion hero-detail-import
import { HeroDetailComponent } from './hero-detail.component';
// #enddocregion hero-detail-import
import { HeroService } from './hero.service';
@Component({
@ -16,8 +11,8 @@ import { HeroService } from './hero.service';
template: `
<h1>{{title}}</h1>
<nav>
<a [routerLink]="['Dashboard']">Dashboard</a>
<a [routerLink]="['Heroes']">Heroes</a>
<a [routerLink]="['/dashboard']" routerLinkActive="active">Dashboard</a>
<a [routerLink]="['/heroes']" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
@ -27,32 +22,9 @@ import { HeroService } from './hero.service';
// #enddocregion style-urls
directives: [ROUTER_DIRECTIVES],
providers: [
ROUTER_PROVIDERS,
HeroService
]
})
@RouteConfig([
// #docregion dashboard-route
{
path: '/dashboard',
name: 'Dashboard',
component: DashboardComponent,
useAsDefault: true
},
// #enddocregion dashboard-route
// #docregion hero-detail-route
{
path: '/detail/:id',
name: 'HeroDetail',
component: HeroDetailComponent
},
// #enddocregion hero-detail-route
{
path: '/heroes',
name: 'Heroes',
component: HeroesComponent
}
])
export class AppComponent {
title = 'Tour of Heroes';
}

View File

@ -0,0 +1,37 @@
// #docregion
import { provideRouter, RouterConfig } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
// #docregion hero-detail-import
import { HeroDetailComponent } from './hero-detail.component';
// #enddocregion hero-detail-import
export const routes: RouterConfig = [
// #docregion redirect-route
{
path: '',
redirectTo: '/dashboard',
terminal: true
},
// #enddocregion redirect-route
// #docregion dashboard-route
{
path: 'dashboard',
component: DashboardComponent
},
// #enddocregion dashboard-route
// #docregion hero-detail-route
{
path: 'detail/:id',
component: HeroDetailComponent
},
// #enddocregion hero-detail-route
{
path: 'heroes',
component: HeroesComponent
}
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];

View File

@ -0,0 +1,14 @@
// #docregion
import { provideRouter, RouterConfig } from '@angular/router';
import { HeroesComponent } from './heroes.component';
const routes: RouterConfig = [
{
path: '/heroes',
component: HeroesComponent
}
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];

View File

@ -0,0 +1,32 @@
// #docregion
import { provideRouter, RouterConfig } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
// #docregion hero-detail-import
import { HeroDetailComponent } from './hero-detail.component';
// #enddocregion hero-detail-import
export const routes: RouterConfig = [
{
path: '',
redirectTo: '/dashboard',
terminal: true
},
{
path: 'dashboard',
component: DashboardComponent
},
{
path: 'detail/:id',
component: HeroDetailComponent
},
{
path: 'heroes',
component: HeroesComponent
}
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];

View File

@ -2,7 +2,7 @@
// #docregion
import { Component, OnInit } from '@angular/core';
// #docregion import-router
import { Router } from '@angular/router-deprecated';
import { Router } from '@angular/router';
// #enddocregion import-router
import { Hero } from './hero';
@ -36,7 +36,7 @@ export class DashboardComponent implements OnInit {
// #docregion goto-detail
gotoDetail(hero: Hero) {
let link = ['HeroDetail', { id: hero.id }];
let link = ['/detail', hero.id];
this.router.navigate(link);
}
// #enddocregion goto-detail

View File

@ -1,11 +1,11 @@
// #docplaster
// #docregion
// #docregion import-oninit, v2
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
// #enddocregion import-oninit
// #docregion import-route-params
import { RouteParams } from '@angular/router-deprecated';
// #enddocregion import-route-params
// #docregion import-activated-route
import { ActivatedRoute } from '@angular/router';
// #enddocregion import-activated-route
import { Hero } from './hero';
// #docregion import-hero-service
@ -23,27 +23,36 @@ import { HeroService } from './hero.service';
})
// #enddocregion extract-template
// #docregion implement
export class HeroDetailComponent implements OnInit {
export class HeroDetailComponent implements OnInit, OnDestroy {
// #enddocregion implement
hero: Hero;
sub: any;
// #docregion ctor
constructor(
private heroService: HeroService,
private routeParams: RouteParams) {
private route: ActivatedRoute) {
}
// #enddocregion ctor
// #docregion ng-oninit
ngOnInit() {
// #docregion get-id
let id = +this.routeParams.get('id');
// #enddocregion get-id
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.heroService.getHero(id)
.then(hero => this.hero = hero);
});
// #enddocregion get-id
}
// #enddocregion ng-oninit
// #docregion ng-ondestroy
ngOnDestroy() {
this.sub.unsubscribe();
}
// #enddocregion ng-ondestroy
// #docregion go-back
goBack() {
window.history.back();

View File

@ -1,7 +1,7 @@
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router-deprecated';
import { Router } from '@angular/router';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@ -36,7 +36,7 @@ export class HeroesComponent implements OnInit {
onSelect(hero: Hero) { this.selectedHero = hero; }
gotoDetail() {
this.router.navigate(['HeroDetail', { id: this.selectedHero.id }]);
this.router.navigate(['/detail', this.selectedHero.id]);
}
// #docregion heroes-component-renaming
}

View File

@ -1,5 +1,9 @@
// #docregion
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
bootstrap(AppComponent);
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS
]);

View File

@ -3,7 +3,7 @@
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2].*"
"!**/*.[1,2,3].*"
],
"tags": ["tutorial", "tour", "heroes", "router"]
}

View File

@ -15,7 +15,7 @@ figure.image-display
We'll add Angulars *Component Router* to our app to satisfy these requirements.
.l-sub-section
:marked
The [Routing and Navigation](../guide/router-deprecated.html) chapter covers the router in more detail
The [Routing and Navigation](../guide/router.html) chapter covers the router in more detail
than we will in this tutorial.
p Run the #[+liveExampleLink2('', 'toh-5')] for this part.
@ -146,47 +146,42 @@ code-example(language="bash").
.callout.is-important
header base href is essential
:marked
See the *base href* section of the [Router](../guide/router-deprecated.html#!#base-href) chapter to learn why this matters.
See the *base href* section of the [Router](../guide/router.html#!#base-href) chapter to learn why this matters.
:marked
### Make the router available.
The *Component Router* is a service. Like any service, we have to import it and make it
available to the application by adding it to the `providers` array.
The Angular router is a combination of multiple provided services (`provideRouter`), multiple directives (`ROUTER_DIRECTIVES`),
and a configuration (`RouterConfig`). We'll configure our routes first:
The Angular router is a combination of multiple services (`ROUTER_PROVIDERS`), multiple directives (`ROUTER_DIRECTIVES`),
and a configuration decorator (`RouteConfig`). We'll import them all together:
+makeExample('toh-5/ts/app/app.component.2.ts', 'import-router', 'app/app.component.ts (router imports)')(format=".")
:marked
Next we update the `directives` and `providers` metadata arrays to *include* the router assets.
+makeExample('toh-5/ts/app/app.component.2.ts', 'directives-and-providers', 'app/app.component.ts (directives and providers)')(format=".")
:marked
Notice that we also removed the `HeroesComponent` from the `directives` array.
`AppComponent` no longer shows heroes; that will be the router's job.
We'll soon remove `<my-heroes>` from the template too.
### Configure and add the router
### Add and configure the router
The `AppComponent` doesn't have a router yet. We'll use the `@RouteConfig` decorator to simultaneously
(a) assign a router to the component and (b) configure that router with *routes*.
Our application doesn't have a router yet. We'll create a configuration file for our routes that
does two things
(a) configure that router with *routes*. (b) provide an export to add the router to our bootstrap
*Routes* tell the router which views to display when a user clicks a link or
pastes a URL into the browser address bar.
Let's define our first route, a route to the `HeroesComponent`.
+makeExample('toh-5/ts/app/app.component.2.ts', 'route-config', 'app/app.component.ts (RouteConfig for heroes)')(format=".")
+makeExample('toh-5/ts/app/app.routes.2.ts', '', 'app/app.routes.ts')(format=".")
:marked
`@RouteConfig` takes an array of *route definitions*.
The `RouterConfig` is an array of *route definitions*.
We have only one route definition at the moment but rest assured, we'll add more.
This *route definition* has three parts:
This *route definition* has two parts:
* **path**: the router matches this route's path to the URL in the browser address bar (`/heroes`).
* **name**: the official name of the route; it *must* begin with a capital letter to avoid confusion with the *path* (`Heroes`).
* **component**: the component that the router should create when navigating to this route (`HeroesComponent`).
.l-sub-section
:marked
Learn more about defining routes with @RouteConfig in the [Routing](../guide/router-deprecated.html) chapter.
Learn more about defining routes with RouterConfig in the [Routing](../guide/router.html) chapter.
:marked
### Make the router available.
The *Component Router* is a service. We have to import our `APP_ROUTER_PROVIDERS` which
contains our configured router and make it available to the application by adding it to
the `bootstrap` array.
+makeExample('toh-5/ts/app/main.ts', '', 'app/main.ts')(format=".")
:marked
### Router Outlet
@ -210,11 +205,11 @@ code-example(language="bash").
that tells the router where to navigate when the user clicks the link.
We define a *routing instruction* with a *link parameters array*.
The array only has one element in our little sample, the quoted ***name* of the route** to follow.
Looking back at the route configuration, we confirm that `'Heroes'` is the name of the route to the `HeroesComponent`.
The array only has one element in our little sample, the quoted ***path* of the route** to follow.
Looking back at the route configuration, we confirm that `'/heroes'` is the path of the route to the `HeroesComponent`.
.l-sub-section
:marked
Learn about the *link parameters array* in the [Routing](../guide/router-deprecated.html#link-parameters-array) chapter.
Learn about the *link parameters array* in the [Routing](../guide/router.html#link-parameters-array) chapter.
:marked
Refresh the browser. We see only the app title. We don't see the heroes list.
.l-sub-section
@ -245,23 +240,27 @@ code-example(language="bash").
Well come back and make it more useful later.
### Configure the dashboard route
Go back to `app.component.ts` and teach it to navigate to the dashboard.
Go back to `app.routes.ts` and teach it to navigate to the dashboard.
Import the `DashboardComponent` so we can reference it in the dashboard route definition.
Add the following `'Dashboard'` route definition to the `@RouteConfig` array of definitions.
+makeExample('toh-5/ts/app/app.component.ts','dashboard-route', 'app/app.component.ts (Dashboard route)')(format=".")
Add the following `'Dashboard'` route definition to the `RouterConfig` array of definitions.
+makeExample('toh-5/ts/app/app.routes.1.ts','dashboard-route', 'app/app.routes.ts (Dashboard route)')(format=".")
.l-sub-section
:marked
**useAsDefault**
**Redirect**
We want the app to show the dashboard when it starts and
we want to see a nice URL in the browser address bar that says `/dashboard`.
Remember that the browser launches with `/` in the address bar.
We don't have a route for that path and we'd rather not create one.
We can use a redirect route to make this happen.
+makeExample('toh-5/ts/app/app.routes.1.ts','redirect-route', 'app/app.routes.ts (Redirect route)')(format=".")
.l-sub-section
:marked
Learn about the *redirects* in the [Routing](../guide/router.html#!#redirect) chapter.
Fortunately we can add the `useAsDefault: true` property to the *route definition* and the
router will display the dashboard when the browser URL doesn't match an existing route.
:marked
Finally, add a dashboard navigation link to the template, just above the *Heroes* link.
@ -364,16 +363,16 @@ code-example(format='').
### Configure a Route with a Parameter
Here's the *route definition* we'll use.
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (route to HeroDetailComponent)')(format=".")
+makeExample('toh-5/ts/app/app.routes.1.ts','hero-detail-route', 'app/app.routes.ts (route to HeroDetailComponent)')(format=".")
:marked
The colon (:) in the path indicates that `:id` is a placeholder to be filled with a specific hero `id`
when navigating to the `HeroDetailComponent`.
.l-sub-section
:marked
Of course we have to import the `HeroDetailComponent` before we create this route:
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-import')(format=".")
+makeExample('toh-5/ts/app/app.routes.1.ts','hero-detail-import')(format=".")
:marked
We're finished with the `AppComponent`.
We're finished with the application routes.
We won't add a `'Hero Detail'` link to the template because users
don't click a navigation *link* to view a particular hero.
@ -395,30 +394,36 @@ code-example(format='').
The template won't change. We'll display a hero the same way. The big changes are driven by how we get the hero.
We will no longer receive the hero in a parent component property binding.
The new `HeroDetailComponent` should take the `id` parameter from the router's `RouteParams` service
and use the `HeroService` to fetch the hero with that `id`.
The new `HeroDetailComponent` should take the `id` parameter from the `params` observable
in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`.
We need an import statement to reference the `RouteParams`.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-route-params')(format=".")
We need an import statement to reference the `ActivatedRoute`.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-activated-route')(format=".")
:marked
We import the `HeroService`so we can fetch a hero.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-hero-service')(format=".")
:marked
We import the `OnInit` interface because we'll call the `HeroService` inside the `ngOnInit` component lifecycle hook.
We import the `OnInit` and `OnDestroy` interfaces because we'll call the `HeroService` inside the `ngOnInit` component lifecycle hook
and we'll clean up our `params` subscription in the `ngOnDestroy`.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-oninit')(format=".")
:marked
We inject the both the `RouteParams` service and the `HeroService` into the constructor as we've done before,
We inject the both the `ActivatedRoute` service and the `HeroService` into the constructor as we've done before,
making private variables for both:
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'ctor', 'app/hero-detail.component.ts (constructor)')(format=".")
:marked
We tell the class that we want to implement the `OnInit` interface.
We tell the class that we want to implement the `OnInit` and `OnDestroy` interfaces.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'implement')(format=".")
:marked
Inside the `ngOnInit` lifecycle hook, extract the `id` parameter value from the `RouteParams` service
Inside the `ngOnInit` lifecycle hook, we _subscribe_ to the `params` observable to
extract the `id` parameter value from the `ActivateRoute` service
and use the `HeroService` to fetch the hero with that `id`.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'ng-oninit', 'app/hero-detail.component.ts (ngOnInit)')(format=".")
:marked
Notice how we extract the `id` by calling the `RouteParams.get` method.
Inside the `ngOnDestroy` lifecycle hook, we _unsubscribe_ from the `params` subscription.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'ng-ondestroy', 'app/hero-detail.component.ts (ngOnDestroy)')(format=".")
:marked
Notice how we extract the `id` by calling the `subscribe` method
which will deliver our array of route parameters.
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'get-id')(format=".")
:marked
The hero `id` is a number. Route parameters are *always strings*.
@ -446,7 +451,7 @@ code-example(format='').
:marked
Going back too far could take us out of the application.
That's acceptable in a demo. We'd guard against it in a real application,
perhaps with the [*routerCanDeactivate* hook](../api/router/CanDeactivate-interface.html).
perhaps with the [*CanDeactivate* guard](../api/router/index/CanDeactivate-interface.html).
:marked
Then we wire this method with an event binding to a *Back* button that we add to the bottom of the component template.
+makeExample('toh-5/ts/app/hero-detail.component.html', 'back-button')(format=".")
@ -478,13 +483,13 @@ code-example(format='').
1. pass the array to the router's navigate method.
We wrote *link parameters arrays* in the `AppComponent` for the navigation links.
Those arrays had only one element, the name of the destination route.
Those arrays had only one element, the path of the destination route.
This array has two elements, the ***name*** of the destination route and a ***route parameter object***
This array has two elements, the ***path*** of the destination route and a ***route parameter***
with an `id` field set to the value of the selected hero's `id`.
The two array items align with the ***name*** and ***:id*** token in the parameterized `HeroDetail` route configuration we added to `AppComponent` earlier in the chapter.
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (hero detail route)')(format=".")
The two array items align with the ***path*** and ***:id*** token in the parameterized `HeroDetail` route configuration we added to `app.routes.ts` earlier in the chapter.
+makeExample('toh-5/ts/app/app.routes.1.ts','hero-detail-route', 'app/app.routes.ts (hero detail route)')(format=".")
:marked
The `DashboardComponent` doesn't have the router yet. We obtain it in the usual way:
import the `router` reference and inject it in the constructor (along with the `HeroService`):
@ -615,10 +620,12 @@ figure.image-display
+makeExample('toh-5/ts/app/app.component.css', '', 'app/app.component.css (navigation styles)')
.l-sub-section
:marked
**The *router-link-active* class**
**The *routerLinkActive* directive**
The Angular Router adds the `router-link-active` class to the HTML navigation element
whose route matches the active route. All we have to do is define the style for it. Sweet!
The Angular Router provides a `routerLinkActive` directive we can use to
to add a class to the HTML navigation element whose route matches the active route.
All we have to do is define the style for it. Sweet!
+makeExample('toh-5/ts/app/app.component.3.ts', 'router-link-active', 'app/app.component.ts (active router links)')(format=".")
:marked
Set the `AppComponent`s `styleUrls` property to this CSS file.
+makeExample('toh-5/ts/app/app.component.ts','style-urls', 'app/app.component.ts (styleUrls)')(format=".")
@ -667,6 +674,7 @@ p.
.children
.file app.component.ts
.file app.component.css
.file app.routes.ts
.file dashboard.component.css
.file dashboard.component.html
.file dashboard.component.ts