diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json index 1cb56e65de..50622f9aaf 100644 --- a/public/docs/_examples/package.json +++ b/public/docs/_examples/package.json @@ -31,7 +31,7 @@ "@angular/http": "2.0.0-rc.2", "@angular/platform-browser": "2.0.0-rc.2", "@angular/platform-browser-dynamic": "2.0.0-rc.2", - "@angular/router": "2.0.0-rc.2", + "@angular/router": "3.0.0-alpha.7", "@angular/router-deprecated": "2.0.0-rc.2", "@angular/upgrade": "2.0.0-rc.2", "angular2-in-memory-web-api": "0.0.12", diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts index f86293e823..ac3e36f4ea 100644 --- a/public/docs/_examples/router/e2e-spec.ts +++ b/public/docs/_examples/router/e2e-spec.ts @@ -24,14 +24,18 @@ describe('Router', function () { heroDetail: element(by.css('my-app > undefined > div')), heroDetailTitle: element(by.css('my-app > undefined > div > h3')), + adminHref: hrefEles.get(2), + loginHref: hrefEles.get(3) }; } it('should be able to see the start screen', function () { let page = getPageStruct(); - expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); + expect(page.hrefs.count()).toEqual(4, 'should be two dashboard choices'); expect(page.crisisHref.getText()).toEqual('Crisis Center'); expect(page.heroesHref.getText()).toEqual('Heroes'); + expect(page.adminHref.getText()).toEqual('Crisis Admin'); + expect(page.loginHref.getText()).toEqual('Login'); }); it('should be able to see crises center items', function () { diff --git a/public/docs/_examples/router/ts/app/app.component.2.ts b/public/docs/_examples/router/ts/app/app.component.2.ts index 779d43a063..f1580cf6f8 100644 --- a/public/docs/_examples/router/ts/app/app.component.2.ts +++ b/public/docs/_examples/router/ts/app/app.component.2.ts @@ -3,7 +3,7 @@ // #docregion import { Component } from '@angular/core'; -import { Router, ROUTER_DIRECTIVES } from '@angular/router'; +import { ROUTER_DIRECTIVES } from '@angular/router'; // #enddocregion /* diff --git a/public/docs/_examples/router/ts/app/app.routes.1.ts b/public/docs/_examples/router/ts/app/app.routes.1.ts index 480b4eb95c..8c4caeda55 100644 --- a/public/docs/_examples/router/ts/app/app.routes.1.ts +++ b/public/docs/_examples/router/ts/app/app.routes.1.ts @@ -16,11 +16,11 @@ import { HeroDetailComponent } from './heroes/hero-detail.component'; // #docregion route-config export const routes: RouterConfig = [ // #docregion route-defs - { path: '/crisis-center', component: CrisisCenterComponent }, - { path: '/heroes', component: HeroListComponent }, + { path: 'crisis-center', component: CrisisCenterComponent }, + { path: 'heroes', component: HeroListComponent }, // #enddocregion route-defs // #docregion hero-detail-route - { path: '/hero/:id', component: HeroDetailComponent } + { path: 'hero/:id', component: HeroDetailComponent } // #enddocregion hero-detail-route ]; diff --git a/public/docs/_examples/router/ts/app/app.routes.2.ts b/public/docs/_examples/router/ts/app/app.routes.2.ts index 57c069c81d..d0d64e2ea0 100644 --- a/public/docs/_examples/router/ts/app/app.routes.2.ts +++ b/public/docs/_examples/router/ts/app/app.routes.2.ts @@ -9,8 +9,8 @@ import { HeroListComponent } from './hero-list.component'; // #docregion route-config export const routes: RouterConfig = [ - { path: '/crisis-center', component: CrisisListComponent }, - { path: '/heroes', component: HeroListComponent } + { path: 'crisis-center', component: CrisisListComponent }, + { path: 'heroes', component: HeroListComponent } ]; export const APP_ROUTER_PROVIDERS = [ diff --git a/public/docs/_examples/router/ts/app/app.routes.3.ts b/public/docs/_examples/router/ts/app/app.routes.3.ts index f7becf70ea..c6e9cffc72 100644 --- a/public/docs/_examples/router/ts/app/app.routes.3.ts +++ b/public/docs/_examples/router/ts/app/app.routes.3.ts @@ -7,7 +7,7 @@ import { HeroesRoutes } from './heroes/heroes.routes'; export const routes = [ ...HeroesRoutes, - { path: '/crisis-center', component: CrisisListComponent } + { path: 'crisis-center', component: CrisisListComponent } ]; export const APP_ROUTER_PROVIDERS = [ diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts index 0bd846c600..0c50d6b7df 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts @@ -7,11 +7,11 @@ import { CrisisCenterComponent } from './crisis-center.component'; // #docregion routes export const CrisisCenterRoutes: RouterConfig = [ { - path: '/crisis-center', + path: 'crisis-center', component: CrisisCenterComponent, children: [ - { path: '/', component: CrisisListComponent }, - { path: '/:id', component: CrisisDetailComponent } + { path: ':id', component: CrisisDetailComponent }, + { path: '', component: CrisisListComponent } ] } ]; diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts index 2856544136..43eadbd55e 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts @@ -6,17 +6,20 @@ import { CrisisCenterComponent } from './crisis-center.component'; // #docregion routes export const CrisisCenterRoutes: RouterConfig = [ + // #docregion redirect { - path: '/crisis-center', + path: '', + redirectTo: '/crisis-center', + terminal: true + }, + // #enddocregion redirect + { + path: 'crisis-center', component: CrisisCenterComponent, - index: true, children: [ - { path: '/:id', component: CrisisDetailComponent }, - { path: '/', component: CrisisListComponent, - index: true - } + { path: ':id', component: CrisisDetailComponent }, + { path: '', component: CrisisListComponent } ] } ]; // #enddocregion routes - diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts index 5d7170d500..1f194ac6f7 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts @@ -10,25 +10,28 @@ import { CanDeactivateGuard } from '../interfaces'; export const CrisisCenterRoutes: RouterConfig = [ { - path: '/crisis-center', + path: '', + redirectTo: '/crisis-center', + terminal: true + }, + { + path: 'crisis-center', component: CrisisCenterComponent, - index: true, children: [ // #docregion admin-route-no-guard { - path: '/admin', + path: 'admin', component: CrisisAdminComponent }, // #enddocregion admin-route-no-guard { - path: '/:id', + path: ':id', component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard] }, { - path: '/', - component: CrisisListComponent, - index: true + path: '', + component: CrisisListComponent } ] } @@ -40,7 +43,7 @@ export const CrisisCenterRoutes: RouterConfig = [ import { AuthGuard } from '../auth.guard'; { - path: '/admin', + path: 'admin', component: CrisisAdminComponent, canActivate: [AuthGuard] } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts index 0e16b01d1c..f238e132a7 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts @@ -11,25 +11,28 @@ import { AuthGuard } from '../auth.guard'; export const CrisisCenterRoutes: RouterConfig = [ { - path: '/crisis-center', + path: '', + redirectTo: '/crisis-center', + terminal: true + }, + { + path: 'crisis-center', component: CrisisCenterComponent, - index: true, children: [ { - path: '/admin', + path: 'admin', component: CrisisAdminComponent, canActivate: [AuthGuard] }, { - path: '/:id', + path: ':id', component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard] }, // #docregion default-route { - path: '/', - component: CrisisListComponent, - index: true + path: '', + component: CrisisListComponent } // #enddocregion default-route ] @@ -42,7 +45,7 @@ export const CrisisCenterRoutes: RouterConfig = [ import { AuthGuard } from '../auth.guard'; { - path: '/admin', + path: 'admin', component: CrisisAdminComponent, canActivate: [AuthGuard] } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts index f9d0a26f8d..a924aa5771 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts @@ -10,25 +10,29 @@ import { AuthGuard } from '../auth.guard'; export const CrisisCenterRoutes: RouterConfig = [ { - path: '/crisis-center', + path: '', + redirectTo: '/crisis-center', + terminal: true + }, + { + path: 'crisis-center', component: CrisisCenterComponent, - index: true, children: [ // #docregion admin-route { - path: '/admin', + path: 'admin', component: CrisisAdminComponent, canActivate: [AuthGuard] }, // #enddocregion admin-route { - path: '/:id', + path: ':id', component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard] }, - { path: '/', - component: CrisisListComponent, - index: true + { + path: '', + component: CrisisListComponent } ] } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts index 72e2500669..20597752cc 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts @@ -14,7 +14,7 @@ const CRISES = [ let crisesPromise = Promise.resolve(CRISES); // #docregion -import {Injectable} from '@angular/core'; +import { Injectable } from '@angular/core'; @Injectable() export class CrisisService { diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts index 9f765d0ad0..bfa9ca6d0f 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts @@ -56,7 +56,7 @@ export class HeroDetailComponent implements OnInit, OnDestroy { let heroId = this.hero ? this.hero.id : null; // Pass along the hero id if available // so that the HeroList component can select that hero. - this.router.navigate(['/heroes'], { queryParams: { id: `${heroId}`, foo: 'foo' } }); + this.router.navigate(['/heroes'], { queryParams: { id: heroId, foo: 'foo' } }); } // #enddocregion gotoHeroes-navigate } diff --git a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts b/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts index c4463cc8a1..80018d1f26 100644 --- a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts +++ b/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts @@ -4,9 +4,9 @@ import { HeroListComponent } from './hero-list.component'; import { HeroDetailComponent } from './hero-detail.component'; export const HeroesRoutes: RouterConfig = [ - { path: '/heroes', component: HeroListComponent }, + { path: 'heroes', component: HeroListComponent }, // #docregion hero-detail-route - { path: '/hero/:id', component: HeroDetailComponent } + { path: 'hero/:id', component: HeroDetailComponent } // #enddocregion hero-detail-route ]; // #enddocregion diff --git a/public/docs/_examples/router/ts/app/login.component.ts b/public/docs/_examples/router/ts/app/login.component.ts index 898ca2d4c5..2790bfc79d 100755 --- a/public/docs/_examples/router/ts/app/login.component.ts +++ b/public/docs/_examples/router/ts/app/login.component.ts @@ -4,7 +4,6 @@ import { Router } from '@angular/router'; import { AuthService } from './auth.service'; @Component({ - selector: 'login', template: `

LOGIN

{{message}}

@@ -25,7 +24,7 @@ export class LoginComponent { } login() { - this.message = "Trying to log in ..."; + this.message = 'Trying to log in ...'; this.authService.login().subscribe(() => { this.setMessage(); diff --git a/public/docs/_examples/router/ts/app/login.routes.ts b/public/docs/_examples/router/ts/app/login.routes.ts index 71ad3cf9e4..6dabba376f 100644 --- a/public/docs/_examples/router/ts/app/login.routes.ts +++ b/public/docs/_examples/router/ts/app/login.routes.ts @@ -4,7 +4,7 @@ import { AuthService } from './auth.service'; import { LoginComponent } from './login.component'; export const LoginRoutes = [ - { path: '/login', component: LoginComponent } + { path: 'login', component: LoginComponent } ]; export const AUTH_PROVIDERS = [AuthGuard, AuthService]; diff --git a/public/docs/_examples/systemjs.config.plunker.js b/public/docs/_examples/systemjs.config.plunker.js index 0479c88703..bf2fc3de68 100644 --- a/public/docs/_examples/systemjs.config.plunker.js +++ b/public/docs/_examples/systemjs.config.plunker.js @@ -6,7 +6,7 @@ (function(global) { var ngVer = '@2.0.0-rc.2'; // lock in the angular package version; do not let it float to current! - var routerVer = '@3.0.0-alpha.3'; // lock router version + var routerVer = '@3.0.0-alpha.7'; // lock router version //map tells the System loader where to look for things var map = { diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index 853f735d65..a92d0386c0 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -47,7 +47,7 @@ include ../_util-fns * navigating under [program control](#navigate) * embedding critical information in the URL with [route parameters](#route-parameters) * add [child routes](#child-routing-component) under a feature section - * setting an [index route](#index) as the default + * [redirecting](#redirect) from one route to another * confirming or canceling navigation with [guards](#guards) * [CanActivate](#can-activate-guard) to prevent navigation to a route * [CanDeactivate](#can-deactivate-deactivate) to prevent navigation away from the current route @@ -100,6 +100,9 @@ include ../_util-fns The `RouterConfig` is an array of *routes* that describe how to navigate. Each *Route* maps a URL `path` to a component. + There are no **leading slashes** in our **path**. The router parses and builds the URL for us, + allowing us to use relative and absolute paths when navigating between application views. + The `:id` in the third route is a token for a route parameter. In a URL such as `/hero/42`, "42" is the value of the `id` parameter. The corresponding `HeroDetailComponent` will use that value to find and present the hero whose `id` is 42. @@ -167,7 +170,7 @@ table tr td Route td. - Defines how the router should navigate to a component based on a URL pattern. + Defines how the router should navigate to a component based on a URL pattern. Most routes consist of a path and a component type. tr td RouterOutlet @@ -343,10 +346,10 @@ figure.image-display h4#import Import from the Component Router library :marked - We begin by importing some symbols from the router library. - + We begin by importing some symbols from the router library. + The Component Router is in its own `@angular/router` package. - It's not part of the Angular 2 core. + It's not part of the Angular 2 core. The router is an optional service because not all applications need routing and, depending on your requirements, you may need a different routing library. @@ -371,17 +374,17 @@ h4#define-routes Define routes * *When the application requests navigation to the path `/crisis-center`, create or retrieve an instance of the `CrisisListComponent`, display its view, and update the browser's address location and history with the URL - for that path.* + for that path.* h4#provideRouter Call provideRouter :marked - We pass the route configuration to the `provideRouter` function which returns an array containing the configured + We pass the route configuration to the `provideRouter` function which returns an array containing the configured `Router` service provider ... and some other, unseen providers that the routing library requires. :marked We add the `provideRouter` array to an `APP_ROUTER_PROVIDERS` array and export it. - We could add *additional* service providers to `APP_ROUTER_PROVIDERS` — + We could add *additional* service providers to `APP_ROUTER_PROVIDERS` — providers that are specific to our routing configuration. We don't have any yet. We will have some later in this chapter. @@ -398,10 +401,10 @@ h4#register-providers Register routing in bootstrap and pass it as the second parameter of the `bootstrap` function. +makeExample('router/ts/app/main.1.ts','all', 'main.ts')(format=".") :marked - Providing the router providers at the root makes the Router available everywhere in our application. + Providing the router providers at bootstrap makes the Router available everywhere in our application. .alert.is-important :marked - We must register router providers in `bootstrap`. + We must register router providers in `bootstrap`. We cannot wait to do it in `AppComponent`. h3#shell The AppComponent shell @@ -424,7 +427,7 @@ h3#router-outlet RouterOutlet .l-sub-section :marked A template may hold exactly one ***unnamed*** ``. - 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 RouterLink binding :marked @@ -434,7 +437,7 @@ h3#router-link RouterLink binding The template expression to the right of the equals (=) returns a *link parameters array*. A link parameters array holds the ingredients for router navigation: - * the *path* of the route to the destination component + * the *path* of the route to the destination component * optional route and query parameters that go into the route URL The arrays in this example each have a single string parameter, the path of a route that @@ -445,7 +448,7 @@ h3#router-link RouterLink binding h3#router-directives ROUTER_DIRECTIVES :marked - `RouterLink` and `RouterOutlet` are directives in the `ROUTER_DIRECTIVES` collection. + `RouterLink` and `RouterOutlet` are directives in the `ROUTER_DIRECTIVES` collection. Remember to add them to the `directives` array of the `@Component` metadata. +makeExample('router/ts/app/app.component.1.ts','directives')(format=".") :marked @@ -574,8 +577,8 @@ figure.image-display ### *Hero* feature route configuration - 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 `hero.routes.ts` in the `heroes` folder like this: +makeExample('router/ts/app/heroes/heroes.routes.ts','', 'app/heroes/heroes.routes.ts')(format=".") :marked @@ -610,7 +613,7 @@ code-example(format="." language="bash"). h3#merge-hero-routes Merge hero routes into application routes :marked - Our application doesn't know about our hero routes yet. + Our application doesn't know about our hero routes yet. We'll need to merge them into the application routes we defined in `app.routes.ts`. Update `app.routes.ts` as follows: @@ -626,7 +629,7 @@ h3#merge-hero-routes Merge hero routes into application routes h3#navigate Navigate to hero detail imperatively :marked - *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. Instead, when the user *clicks* a hero in the list, we'll *command* the router @@ -700,7 +703,7 @@ a#hero-detail-ctor .l-sub-section :marked - Learn about the `ngOnInit` and `ngOnDestroy` methods in the + Learn about the `ngOnInit` and `ngOnDestroy` methods in the [Lifecycle Hooks](lifecycle-hooks.html) chapter. h4#reuse Observable params and component re-use @@ -708,11 +711,11 @@ h4#reuse Observable params and component re-use In this example, we subscribe to the route params `Observable`. That implies that the route params can change during the lifetime of this component. - They might. By default, the router reuses a component instance when it re-navigates to the same component type + They might. By default, the router reuses a component instance when it re-navigates to the same component type without visiting a different component first. The parameters can change between each re-use. - Suppose a parent component navigation bar had "forward" and "back" buttons - that scrolled through the list of heroes. + Suppose a parent component navigation bar had "forward" and "back" buttons + that scrolled through the list of heroes. Each click navigated imperatively to the `HeroDetailComponent` with the next or previous `id`. We don't want the router to remove the current `HeroDetailComponent` instance from the @@ -720,13 +723,13 @@ h4#reuse Observable params and component re-use That could be visibly jarring. Better to simply re-use the same component instance and update the parameter. - But `ngOnInit` is only called once per instantiation. + But `ngOnInit` is only called once per instantiation. We need a way to detect when the route parameters change from _within the same instance_. The observable `params` property handles that beautifully. h4#snapshot Snapshot: the no-observable alternative :marked - This application won't reuse the `HeroDetailComponent`. + This application won't reuse the `HeroDetailComponent`. We always return to the hero list to select another hero to view. There's no way to navigate from hero detail to hero detail without visiting the list component in between. @@ -742,8 +745,8 @@ h4#snapshot Snapshot: the no-observable alternative .l-sub-section :marked **Remember:** we only get the _initial_ value of the parameters with this technique. - Stick with the observable `params` approach if there's even a chance that we might navigate - to this component multiple times in a row. + Stick with the observable `params` approach if there's even a chance that we might navigate + to this component multiple times in a row. We are leaving the observable `params` strategy in place just in case. h3#nav-to-list Navigating back to the list component @@ -841,7 +844,7 @@ h3#nav-to-list Navigating back to the list component * Our `CrisisService` is only needed within the *Crisis Center* feature area. We should limit access to it to that feature area. - * Changes to a sub-module such as *Crisis Center* shouldn't provoke changes to the `AppComponent` or + * Changes to a sub-module such as *Crisis Center* shouldn't provoke changes to the `AppComponent` or any other feature's component. We need to [*separate our concerns*](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html). @@ -861,7 +864,7 @@ h3#nav-to-list Navigating back to the list component * each area with its own area root component * each area root component with its own router-outlet and child routes * area routes rarely (if ever) cross - + If we had many feature areas, their component trees might look like this: figure.image-display @@ -883,8 +886,8 @@ h3#child-routing-component Child Routing Component * It is dead simple — simpler even than the `AppComponent` template. It has no content, no links, just a `` for the *Crisis Center* child views. - 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. + Unlike `AppComponent` (and most other components), it **lacks a selector**. + It doesn't need one. We don't *embed* this component in a parent template. We *navigate* to it from the outside, via the router. .l-sub-section :marked @@ -907,9 +910,9 @@ h3#child-routing-component Child Routing Component First we can evolve the service independently of the rest of the application without fear of breaking what should be unrelated modules. - + Second, we can delay loading this service into memory until we need it. - We can remove it from the application launch bundle, + We can remove it from the application launch bundle, reducing the size of the initial payload and improving performance. We can load it optionally, asynchronously with the other *Crisis Center* components if and when the user begins that workflow. @@ -924,11 +927,11 @@ h3#child-routing-component Child Routing Component We create a `crisis-center.routes.ts` file as we did the `heroes.routes.ts` file. But this time we define **child routes** *within* the parent `/crisis-center` route. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', 'routes', 'app/crisis-center/crisis-center.routes.ts (Routes)' )(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', 'routes', 'app/crisis-center/crisis-center.routes.ts (Routes)' )(format='.') :marked Notice that the parent `/crisis-center` route has a `children` property with an array of two routes. - These two routes navigate to the two *Crisis Center* child components, + These two routes navigate to the two *Crisis Center* child components, `CrisisListComponent` and `CrisisDetailComponent`. There are some *important differences* in the treatment of these routes. @@ -939,8 +942,8 @@ h3#child-routing-component Child Routing Component Second, the child paths *extend* the path of their parent route. Normally paths that begin with `/` refer to the root of the application. - Here they are appended to the path to the `CrisisCenterComponent`. - + Here they are appended to the path to the `CrisisCenterComponent`. + To write an URL that navigates to the `CrisisListComponent`, we'd append its child route path, `/`, to `/crisis-center`. @@ -952,7 +955,7 @@ code-example(format=""). :marked Here's the complete `crisis-center.routes.ts` with its imports. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', '', 'app/crisis-center/crisis-center.routes.ts' )(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', '', 'app/crisis-center/crisis-center.routes.ts' )(format='.') h3#merge-crisis-routes Merge crisis routes into the application routes :marked @@ -960,10 +963,10 @@ h3#merge-crisis-routes Merge crisis routes into the application routes by merging the crisis routes into the app routes: +makeExample('router/ts/app/app.routes.4.ts', '', 'app/app.routes.ts' )(format='.') :marked - We used the spread operator again (...) to insert the crisis routes array. + We used the spread operator again (...) to insert the crisis routes array. -a#index -h3#default-route Setting default routes +a#redirect +h3#redirect Redirecting routes :marked When the application launches, the initial URL in the browser bar is something like: code-example(format=""). @@ -973,15 +976,24 @@ code-example(format=""). The user must click one of the navigation links to trigger a navigation and display something. We want the application to display the list of crises as it would if we pasted `localhost:3000/crisis-center/` into the address bar. - This is our *default* route. + This is our intended default route. - We can arrange for that behavior in several ways. - One way is to add `index: true` to each route on the path to the default component. + We can arrange for that behavior in several ways. + One way is to use a `redirect` to transparently navigate from one route to another. - In our example, we'll add `index: true` to two routes: - 1. The parent route for the `CrisisCenterComponent` - 1. The child route for the `CrisisListComponent` + In our example, we'll add a route to match our initial URL and redirect to our `crisis-center` route: ++makeExample('router/ts/app/crisis-center/crisis-center.routes.2.ts', 'redirect', 'app/crisis-center/crisis-center.routes.ts (redirect route)' )(format='.') +:marked + Since we only want to redirect when our path specifically matches `''`, we've added an extra configuration + to our route using `terminal: true`. Mainly for redirects, the `terminal` property gives us more control over + when the router should continue matching our URL against our defined routes. + +.l-sub-section + :marked + We'll discuss redirects further in a future update to this chapter. + +:marked The updated route definitions look like this: +makeExample('router/ts/app/crisis-center/crisis-center.routes.2.ts', 'routes', 'app/crisis-center/crisis-center.routes.ts (Routes v.2)' )(format='.') @@ -1006,18 +1018,18 @@ h2#guards Route Guards :marked The guard can also tell the router to navigate elsewhere, effectively canceling the current navigation. :marked - The guard *might* return its boolean answer synchronously. + The guard *might* return its boolean answer synchronously. But in many cases, the guard can't produce an answer synchronously. The guard could ask the user a question, save changes to the server, or fetch fresh data. These are all asynchronous operations. Accordingly, a routing guard can return an `Observable` and the - router will wait for the observable to resolve to `true` or `false. + router will wait for the observable to resolve to `true` or `false. The router supports two kinds of guards: - - 1. [CanActivate](../api/router/index/CanActivate-interface.html) to mediate navigation *to* a route. - + + 1. [CanActivate](../api/router/index/CanActivate-interface.html) to mediate navigation *to* a route. + 2. [CanDeactivate](../api/router/index/CanDeactivate-interface.html) to mediate navigation *away* from the current route. .l-sub-section @@ -1027,7 +1039,7 @@ h2#guards Route Guards We can have multiple guards at every level of a routing hierarchy. The router checks the `CanDeactive` guards first, from deepest child route to the top. Then it checks the `CanActivate` guards from the top down to the deepest child route. - If _any_ guard returns false, pending guards that have not completed will be canceled, + If _any_ guard returns false, pending guards that have not completed will be canceled, and the entire navigation is canceled. Let's look at some examples. @@ -1058,27 +1070,27 @@ h3#can-activate-guard CanActivate: requiring authentication +makeExample('router/ts/app/crisis-center/crisis-center.routes.3.ts', 'admin-route-no-guard', 'crisis-center.routes.ts (admin route)')(format=".") :marked And we add a link to the `AppComponent` shell that users can click to get to this feature. -+makeExample('router/ts/app/app.component.4.ts', 'template', 'app/app.component.ts (template)')(format=".") ++makeExample('router/ts/app/app.component.4.ts', 'template', 'app/app.component.ts (template)')(format=".") :marked #### Guard the admin feature - Currently every route within our *Crisis Center* is open to everyone. + Currently every route within our *Crisis Center* is open to everyone. The new *admin* feature should be accessible only to authenticated users. - We could hide the link until the user logs in. But that's tricky and difficult to maintain. + We could hide the link until the user logs in. But that's tricky and difficult to maintain. Instead we'll write a `CanActivate` guard to redirect anonymous users to the login page when they try to reach the admin component. - + This is a general purpose guard — we can imagine other features that require authenticated users — - so we create an `auth.guard.ts` in the application root folder. - + so we create an `auth.guard.ts` in the application root folder. + At the moment we're interested in seeing how guards work so our first version does nothing useful. It simply logs to console and `returns` true immediately, allowing navigation to proceed: -+makeExample('router/ts/app/auth.guard.1.ts', '', 'app/auth.guard.ts')(format=".") ++makeExample('router/ts/app/auth.guard.1.ts', '', 'app/auth.guard.ts')(format=".") :marked - Next we open `crisis-center.routes.ts `, import the `AuthGuard` class, and + Next we open `crisis-center.routes.ts `, import the `AuthGuard` class, and update the admin route with a `CanActivate` guard property that references it: +makeExample('router/ts/app/crisis-center/crisis-center.routes.ts', 'admin-route', 'crisis-center.routes.ts (guarded admin route)')(format=".") - Our admin feature is now protected by the guard, albeit protected poorly. + Our admin feature is now protected by the guard, albeit protected poorly. :marked #### Teach *AuthGuard* to authenticate Let's make our `AuthGuard` at least pretend to authenticate. @@ -1087,24 +1099,24 @@ h3#can-activate-guard CanActivate: requiring authentication Here's a demo `AuthService`: +makeExample('router/ts/app/auth.service.ts', '', 'app/auth.service.ts')(format=".") :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. Its `login` method simulates an API call to an external service by returning an observable that resolves successfully after a short pause. Let's revise our `AuthGuard` to call it. +makeExample('router/ts/app/auth.guard.ts', '', 'app/auth.guard.ts (v.2)')(format=".") :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. - This guard returns a synchronous boolean result. + This guard returns a synchronous boolean result. If the user is logged in, it returns true and the navigation continues. If the user is not logged in, we tell the router to navigate to a login page — a page we haven't created yet. This secondary navigation automatically cancels the current navigation; we return `false` just to be clear about that. #### Add the *LoginComponent* - We need a `LoginComponent` for the user to log in to the app. + We need a `LoginComponent` for the user to log in to the app. There is nothing new about this component or the way we wire it into the router configuration. Here is the pertinent code, offered without comment: +makeTabs( @@ -1427,7 +1439,7 @@ code-example(format="." language="bash"). :marked ## Wrap Up We've covered a lot of ground in this chapter and the application is too big to reprint here. - Please visit the [live example](/resources/live-examples/router/ts/plnkr.html) and + Please visit the [live example](/resources/live-examples/router/ts/plnkr.html) and where you can download the final source code. .l-main-section