From 6efdbed2a097a4d77fb73bcec33de16bde422df4 Mon Sep 17 00:00:00 2001 From: Brandon Roberts Date: Mon, 11 Apr 2016 21:58:07 -0500 Subject: [PATCH] docs(cookbook): Added router cookbook --- public/docs/_examples/cb-router/e2e-spec.js | 49 ++++++ public/docs/_examples/cb-router/ts/.gitignore | 1 + .../cb-router/ts/app/app.component.1.ts | 27 +++ .../cb-router/ts/app/app.component.2.ts | 21 +++ .../cb-router/ts/app/app.component.3.ts | 16 ++ .../cb-router/ts/app/app.component.4.ts | 32 ++++ .../cb-router/ts/app/app.component.ts | 38 +++++ .../cb-router/ts/app/home.component.1.ts | 10 ++ .../cb-router/ts/app/home.component.2.ts | 21 +++ .../cb-router/ts/app/home.component.ts | 34 ++++ .../docs/_examples/cb-router/ts/app/main.ts | 9 + .../cb-router/ts/app/navigating.component.ts | 13 ++ .../ts/app/restricted.component.1.ts | 3 + .../cb-router/ts/app/restricted.component.ts | 28 ++++ .../ts/app/unauthorized.component.ts | 10 ++ .../cb-router/ts/example-config.json | 0 public/docs/_examples/cb-router/ts/index.html | 40 +++++ public/docs/_examples/cb-router/ts/plnkr.json | 9 + public/docs/ts/latest/cookbook/_data.json | 5 + public/docs/ts/latest/cookbook/router.jade | 156 ++++++++++++++++++ 20 files changed, 522 insertions(+) create mode 100644 public/docs/_examples/cb-router/e2e-spec.js create mode 100644 public/docs/_examples/cb-router/ts/.gitignore create mode 100644 public/docs/_examples/cb-router/ts/app/app.component.1.ts create mode 100644 public/docs/_examples/cb-router/ts/app/app.component.2.ts create mode 100644 public/docs/_examples/cb-router/ts/app/app.component.3.ts create mode 100644 public/docs/_examples/cb-router/ts/app/app.component.4.ts create mode 100644 public/docs/_examples/cb-router/ts/app/app.component.ts create mode 100644 public/docs/_examples/cb-router/ts/app/home.component.1.ts create mode 100644 public/docs/_examples/cb-router/ts/app/home.component.2.ts create mode 100644 public/docs/_examples/cb-router/ts/app/home.component.ts create mode 100644 public/docs/_examples/cb-router/ts/app/main.ts create mode 100644 public/docs/_examples/cb-router/ts/app/navigating.component.ts create mode 100644 public/docs/_examples/cb-router/ts/app/restricted.component.1.ts create mode 100644 public/docs/_examples/cb-router/ts/app/restricted.component.ts create mode 100644 public/docs/_examples/cb-router/ts/app/unauthorized.component.ts create mode 100644 public/docs/_examples/cb-router/ts/example-config.json create mode 100644 public/docs/_examples/cb-router/ts/index.html create mode 100644 public/docs/_examples/cb-router/ts/plnkr.json create mode 100644 public/docs/ts/latest/cookbook/router.jade diff --git a/public/docs/_examples/cb-router/e2e-spec.js b/public/docs/_examples/cb-router/e2e-spec.js new file mode 100644 index 0000000000..32ee71d5ce --- /dev/null +++ b/public/docs/_examples/cb-router/e2e-spec.js @@ -0,0 +1,49 @@ +describe('Router Cookbook Tests', function () { + + + beforeAll(function () { + browser.get(''); + }); + + describe('Subscribing to the router', function() { + // #docregion router-subscription + // ... + + it('should listen for URL updates', function () { + var headers = element.all(by.className('header')); + + expect(headers.first().getText()).toContain('/home'); + }); + // ... + // #enddocregion router-subscription + }); + + describe('Navigating without updating the URL', function() { + // #docregion silent-navigation + // ... + + it('should update the view without updating the URL', function () { + var links = element.all(by.tagName('a')); + links.last().click(); + + var headers = element.all(by.className('header')); + expect(headers.first().getText()).toContain('/restricted'); + expect(headers.last().getText()).toContain('/unauthorized'); + }); + // ... + // #enddocregion silent-navigation + }); + + describe('After navigation', function() { + // #docregion page-title + // ... + + it('should update the browser page title', function () { + var pageTitle = browser.getTitle().then(function(title) { + expect(title).toBe('Home'); + }); + }); + // ... + // #enddocregion page-title + }); +}); diff --git a/public/docs/_examples/cb-router/ts/.gitignore b/public/docs/_examples/cb-router/ts/.gitignore new file mode 100644 index 0000000000..2cb7d2a2e9 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/.gitignore @@ -0,0 +1 @@ +**/*.js diff --git a/public/docs/_examples/cb-router/ts/app/app.component.1.ts b/public/docs/_examples/cb-router/ts/app/app.component.1.ts new file mode 100644 index 0000000000..902c94aa93 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/app.component.1.ts @@ -0,0 +1,27 @@ +// #docregion +import {Component} from 'angular2/core'; +import {Router, RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; +import {HomeComponent} from './home.component'; + +@Component({ + selector: 'my-app', + directives: [ROUTER_DIRECTIVES], + template: ` +

Current URL: {{ url }}

+ + + ` +}) +@RouteConfig([ + { path: '/home', component: HomeComponent, name: 'Home', useAsDefault: true } +]) +export class AppComponent { + url: string; + constructor(router: Router) { + router.subscribe((url: string) => { + this.url = `/${url}`; + }); + } +} diff --git a/public/docs/_examples/cb-router/ts/app/app.component.2.ts b/public/docs/_examples/cb-router/ts/app/app.component.2.ts new file mode 100644 index 0000000000..9fa3b921ff --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/app.component.2.ts @@ -0,0 +1,21 @@ +// #docregion +import {Component} from 'angular2/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; +import {HomeComponent} from './home.component'; +import {NavigatingComponent} from './navigating.component'; + +@Component({ + selector: 'my-app', + directives: [ROUTER_DIRECTIVES, NavigatingComponent], + template: ` + + + + ` +}) +@RouteConfig([ + { path: '/home', component: HomeComponent, name: 'Home', useAsDefault: true } +]) +export class AppComponent {} diff --git a/public/docs/_examples/cb-router/ts/app/app.component.3.ts b/public/docs/_examples/cb-router/ts/app/app.component.3.ts new file mode 100644 index 0000000000..97860bd1d5 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/app.component.3.ts @@ -0,0 +1,16 @@ +// #docregion +import {Component} from 'angular2/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; +import {HomeComponent} from './home.component'; + +@Component({ + selector: 'my-app', + directives: [ROUTER_DIRECTIVES], + template: ` + + ` +}) +@RouteConfig([ + { path: '/home', component: HomeComponent, name: 'Home', useAsDefault: true } +]) +export class AppComponent {} diff --git a/public/docs/_examples/cb-router/ts/app/app.component.4.ts b/public/docs/_examples/cb-router/ts/app/app.component.4.ts new file mode 100644 index 0000000000..026603f02b --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/app.component.4.ts @@ -0,0 +1,32 @@ +// #docregion silent-navigation +import {Component} from 'angular2/core'; +import {Router, RouteConfig, ROUTER_DIRECTIVES, Location} from 'angular2/router'; +import {RestrictedComponent} from './restricted.component'; +import {UnauthorizedComponent} from './unauthorized.component'; + +@Component({ + selector: 'my-app', + directives: [ROUTER_DIRECTIVES], + template: ` +

Current Path: {{ path }}

+

Current URL: {{ url }}

+ + + ` +}) +@RouteConfig([ + { path: '/restricted', component: RestrictedComponent, name: 'Restricted' }, + { path: '/unauthorized', component: UnauthorizedComponent, name: 'Unauthorized' } +]) +export class AppComponent { + url: string; + path: string; + constructor(router: Router, location: Location) { + router.subscribe((url: string) => { + this.url = `${url}`; + this.path = location.path(); + }); + } +} diff --git a/public/docs/_examples/cb-router/ts/app/app.component.ts b/public/docs/_examples/cb-router/ts/app/app.component.ts new file mode 100644 index 0000000000..7dae1a6e7e --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/app.component.ts @@ -0,0 +1,38 @@ +// #docregion +import {Component} from 'angular2/core'; +import {Router, RouteConfig, ROUTER_DIRECTIVES, Location} from 'angular2/router'; +import {RestrictedComponent} from './restricted.component'; +import {UnauthorizedComponent} from './unauthorized.component'; +import {HomeComponent} from './home.component'; +import {NavigatingComponent} from './navigating.component'; + +@Component({ + selector: 'my-app', + directives: [ROUTER_DIRECTIVES, NavigatingComponent], + template: ` +

Router Cookbook

+ +

Current Path: {{ path }}

+

Current URL: {{ url }}

+ + + ` +}) +@RouteConfig([ + { path: '/home', component: HomeComponent, name: 'Home', useAsDefault: true }, + { path: '/restricted', component: RestrictedComponent, name: 'Restricted' }, + { path: '/unauthorized', component: UnauthorizedComponent, name: 'Unauthorized' } +]) +export class AppComponent { + url: string; + path: string; + constructor(router: Router, location: Location) { + router.subscribe((url: string) => { + this.url = `/${url}`; + this.path = location.path(); + }); + } +} diff --git a/public/docs/_examples/cb-router/ts/app/home.component.1.ts b/public/docs/_examples/cb-router/ts/app/home.component.1.ts new file mode 100644 index 0000000000..5d8388a606 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/home.component.1.ts @@ -0,0 +1,10 @@ +// #docregion +import {Component} from 'angular2/core'; + +@Component({ + selector: 'home', + template: ` +

Welcome Home

+ ` +}) +export class HomeComponent {} diff --git a/public/docs/_examples/cb-router/ts/app/home.component.2.ts b/public/docs/_examples/cb-router/ts/app/home.component.2.ts new file mode 100644 index 0000000000..85d67d4052 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/home.component.2.ts @@ -0,0 +1,21 @@ +// #docregion +import {Component} from 'angular2/core'; +import {OnActivate, ComponentInstruction} from 'angular2/router'; + +@Component({ + selector: 'home', + template: ` +

Welcome Home

+ ` +}) +export class HomeComponent implements OnActivate { + + routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) { + return new Promise((resolve) => { + // wait 3 seconds to simulate page load + setTimeout(() => { + resolve(); + }, 3000); + }); + } +} diff --git a/public/docs/_examples/cb-router/ts/app/home.component.ts b/public/docs/_examples/cb-router/ts/app/home.component.ts new file mode 100644 index 0000000000..45724a4999 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/home.component.ts @@ -0,0 +1,34 @@ +// #docplaster +// #docregion +// #docregion page-title +import {Component} from 'angular2/core'; +import {OnActivate, ComponentInstruction} from 'angular2/router'; +// #docregion page-title +import {Title} from 'angular2/platform/browser'; + +@Component({ + selector: 'home', + template: ` +

Welcome Home

+ ` +}) +export class HomeComponent implements OnActivate { + // #docregion page-title + pageTitle: string = 'Home'; + + constructor(private _title: Title) {} + + routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) { + // #docregion page-title + this._title.setTitle(this.pageTitle); + // #enddocregion page-title + + return new Promise((resolve) => { + // wait 3 seconds to simulate page load + setTimeout(() => { + resolve(); + }, 3000); + }); + // #docregion page-title + } +} diff --git a/public/docs/_examples/cb-router/ts/app/main.ts b/public/docs/_examples/cb-router/ts/app/main.ts new file mode 100644 index 0000000000..c01c6898cc --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/main.ts @@ -0,0 +1,9 @@ +// #docregion +import {Title, bootstrap} from 'angular2/platform/browser'; +import {ROUTER_PROVIDERS} from 'angular2/router'; +import {AppComponent} from './app.component'; + +bootstrap(AppComponent, [ + Title, + ROUTER_PROVIDERS +]); diff --git a/public/docs/_examples/cb-router/ts/app/navigating.component.ts b/public/docs/_examples/cb-router/ts/app/navigating.component.ts new file mode 100644 index 0000000000..d26aecc4f9 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/navigating.component.ts @@ -0,0 +1,13 @@ +// #docregion +import {Component} from 'angular2/core'; +import {Router} from 'angular2/router'; + +@Component({ + selector: 'navigating', + template: ` +

LOADING

+ ` +}) +export class NavigatingComponent { + constructor(public router: Router) {} +} diff --git a/public/docs/_examples/cb-router/ts/app/restricted.component.1.ts b/public/docs/_examples/cb-router/ts/app/restricted.component.1.ts new file mode 100644 index 0000000000..20e2de3f52 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/restricted.component.1.ts @@ -0,0 +1,3 @@ +// #docregion silent-url +this._router.navigateByUrl('/unauthorized', true); +// #enddocregion silent-url diff --git a/public/docs/_examples/cb-router/ts/app/restricted.component.ts b/public/docs/_examples/cb-router/ts/app/restricted.component.ts new file mode 100644 index 0000000000..bbc6328d8d --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/restricted.component.ts @@ -0,0 +1,28 @@ +// #docregion +// #docregion silent-navigation +import {Component, OnInit} from 'angular2/core'; +import {Router} from 'angular2/router'; + +@Component({ + selector: 'restricted', + template: ` +

Restricted Content

+ +
Here is some protected content
+ ` +}) +export class RestrictedComponent implements OnInit { + authorized: boolean = false; + constructor(private _router: Router) {} + + ngOnInit() { + if (!this.authorized) { + // #docregion silent-navigation + let unauthorizedInstruction = this._router.generate(['/Unauthorized']); + + this._router.navigateByInstruction(unauthorizedInstruction, true); + // #enddocregion silent-navigation + } + } +} +// #enddocregion silent-navigation diff --git a/public/docs/_examples/cb-router/ts/app/unauthorized.component.ts b/public/docs/_examples/cb-router/ts/app/unauthorized.component.ts new file mode 100644 index 0000000000..87397fc258 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/app/unauthorized.component.ts @@ -0,0 +1,10 @@ +// #docregion silent-navigation +import {Component} from 'angular2/core'; + +@Component({ + selector: 'unauthorized', + template: ` +

You don't have sufficient access to this page.

+ ` +}) +export class UnauthorizedComponent {} diff --git a/public/docs/_examples/cb-router/ts/example-config.json b/public/docs/_examples/cb-router/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/cb-router/ts/index.html b/public/docs/_examples/cb-router/ts/index.html new file mode 100644 index 0000000000..1318189c10 --- /dev/null +++ b/public/docs/_examples/cb-router/ts/index.html @@ -0,0 +1,40 @@ + + + + + Routing + + + + + + + + + + + + + + + + + + + + + Loading app... + + + diff --git a/public/docs/_examples/cb-router/ts/plnkr.json b/public/docs/_examples/cb-router/ts/plnkr.json new file mode 100644 index 0000000000..c345f09ddb --- /dev/null +++ b/public/docs/_examples/cb-router/ts/plnkr.json @@ -0,0 +1,9 @@ +{ + "description": "Routing Cookbook", + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!app/*.[1,2,3,4].*" + ], + "tags":["cookbook", "router"] +} diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index 211e12bfd4..68f7cbd74b 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -26,6 +26,11 @@ "intro": "Render dynamic forms with NgFormModel" }, + "router": { + "title": "Routing", + "intro": "Using the router navigation cycle" + }, + "set-document-title": { "title": "Set the Document Title", "intro": "Setting the document or window title using the Title service." diff --git a/public/docs/ts/latest/cookbook/router.jade b/public/docs/ts/latest/cookbook/router.jade new file mode 100644 index 0000000000..c8403532e7 --- /dev/null +++ b/public/docs/ts/latest/cookbook/router.jade @@ -0,0 +1,156 @@ +include ../_util-fns + + +:marked + This cookbook contains recipes for common router scenarios + for reacting to the router navigation cycle. + +// + .l-sub-section + + +:marked + ## Table of contents + + [Subscribing to router URL updates](#router-subscription) + + [Showing a loading indicator](#loading-indicator) + + [Updating the page title](#page-title) + + [Navigating without updating the URL](#silent-navigation) + + [Parent and child route share data via a service](#parent-child-route-communication) + +:marked + **See the [live example](/resources/live-examples/cb-router/ts/plnkr.html)**. + +.l-main-section + +:marked + ## Subscribing to router URL updates + + The router service provides an observable that emits a URL when the navigation has + successfully completed. The top level router handles navigation, so we + want to subscribe to it for updates. + ++makeTabs( + `cb-router/ts/app/app.component.1.ts, + cb-router/ts/app/home.component.1.ts`, + null, + `app.component.ts, + home.component.ts` + ) + +:marked + ### Test it + + E2E test that the router subscription is updated after navigation completes + ++makeExample('cb-router/e2e-spec.js', 'router-subscription') + +:marked + [Back to top](#top) + +.l-main-section + +:marked + ## Showing a loading indicator until navigation completes + + The `router.navigating` property is a boolean value toggled when the router starts + and finishes navigating. We will use this value to toggle display of a loading + indicator until the router completes the navigation cycle. + ++makeTabs( + `cb-router/ts/app/app.component.2.ts, + cb-router/ts/app/home.component.2.ts, + cb-router/ts/app/navigating.component.ts`, + null, + `app.component.ts, + home.component.ts, + navigating.component.ts` + ) + +:marked + [Back to top](#top) + +.l-main-section + + +:marked + ## Updating the page title using the router + + Using the lifecycle hook `routerOnActivate` combined with the `Title` service, + we can update the browser page title that appears in the window navigation history + and shows up in the back/forward buttons during routing. + + For more information on using the `Title` service, + see the [Document Title Cookbook](../cookbook/set-document-title.html#top) + ++makeTabs( + `cb-router/ts/app/main.ts, + cb-router/ts/app/app.component.3.ts, + cb-router/ts/app/home.component.ts`, + `,,page-title`, + `main.ts, + app.component.ts, + home.component.ts` + ) + +:marked + ### Test it + + E2E test that the page title is updated after navigating + ++makeExample('cb-router/e2e-spec.js', 'page-title') + +:marked + [Back to top](#top) + +.l-main-section + +:marked + ## Navigating without updating the URL + + Sometimes you want to use the router to update the application view + without updating the URL. This keeps the current URL available for bookmarks, + while switching out the content displayed. + ++makeTabs( + `cb-router/ts/app/app.component.4.ts, + cb-router/ts/app/restricted.component.ts, + cb-router/ts/app/unauthorized.component.ts`, + 'silent-navigation, silent-navigation, silent-navigation', + `app.component.ts, + restricted.component.ts, + unauthorized.component.ts` + ) + +.l-sub-section + :marked + An alternate way to trigger silent navigation is to navigate by URL to update the application view. + ++makeExample('cb-router/ts/app/restricted.component.1.ts', 'silent-url', 'restricted.component.ts (alternate)') + +:marked + ### Test it + + Test that the application view updates without changing the current browser URL. + ++makeExample('cb-router/e2e-spec.js', 'silent-navigation') + +:marked + [Back to top](#top) + +.l-main-section + +:marked + ## Parent and child route communicate via a service + + Since our routes are mapped to components, we should use a service to share data + between routes. Just as routes have parent and child routes, we should treat them + the same way to communicate allowing our + [parent and children to communicate via a service](../cookbook/component-communication.html#bidirectional-service) + +:marked + [Back to top](#top)