From 1847550ad17ddd4a20d02b8a96f718e632ab27d1 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Sat, 11 Mar 2017 13:44:25 +0000 Subject: [PATCH] docs(aio): move cookbooks into guide Also update all docs with migrated versions from latest angular.io Fixes #14941 --- .../ajs-quick-reference.md | 2 +- .../{cookbook => guide}/aot-compiler.md | 2 +- aio/content/guide/architecture.md | 2 +- aio/content/guide/attribute-directives.md | 170 ++-- aio/content/guide/browser-support.md | 13 +- .../cb-dependency-injection.md} | 6 +- .../{cookbook/index.md => guide/cb-index.md} | 2 +- aio/content/guide/change-log.md | 42 +- .../component-communication.md | 4 +- .../component-relative-paths.md | 0 aio/content/guide/displaying-data.md | 2 +- .../dynamic-component-loader.md | 2 +- .../{cookbook => guide}/dynamic-form.md | 4 +- .../{cookbook => guide}/form-validation.md | 6 +- aio/content/guide/glossary.md | 2 +- .../hierarchical-dependency-injection.md | 62 +- aio/content/{cookbook => guide}/i18n.md | 0 aio/content/guide/index.md | 2 +- aio/content/guide/learning-angular.md | 2 +- aio/content/guide/npm-packages.md | 119 +-- aio/content/guide/router.md | 776 +++++++++++------- aio/content/guide/security.md | 2 +- aio/content/guide/server-communication.md | 92 ++- .../{cookbook => guide}/set-document-title.md | 0 aio/content/guide/setup.md | 2 +- aio/content/guide/structural-directives.md | 321 +++----- aio/content/guide/template-syntax.md | 455 +++++++--- aio/content/{cookbook => guide}/ts-to-js.md | 4 +- .../{cookbook => guide}/visual-studio-2015.md | 2 +- aio/content/guide/webpack.md | 4 +- aio/content/tutorial/toh-pt1.md | 2 +- aio/content/tutorial/toh-pt2.md | 2 +- aio/content/tutorial/toh-pt3.md | 2 +- aio/content/tutorial/toh-pt4.md | 2 +- aio/content/tutorial/toh-pt5.md | 2 +- aio/content/tutorial/toh-pt6.md | 2 +- 36 files changed, 1207 insertions(+), 907 deletions(-) rename aio/content/{cookbook => guide}/ajs-quick-reference.md (99%) rename aio/content/{cookbook => guide}/aot-compiler.md (99%) rename aio/content/{cookbook/dependency-injection.md => guide/cb-dependency-injection.md} (99%) rename aio/content/{cookbook/index.md => guide/cb-index.md} (99%) rename aio/content/{cookbook => guide}/component-communication.md (99%) rename aio/content/{cookbook => guide}/component-relative-paths.md (100%) rename aio/content/{cookbook => guide}/dynamic-component-loader.md (99%) rename aio/content/{cookbook => guide}/dynamic-form.md (97%) rename aio/content/{cookbook => guide}/form-validation.md (98%) rename aio/content/{cookbook => guide}/i18n.md (100%) rename aio/content/{cookbook => guide}/set-document-title.md (100%) rename aio/content/{cookbook => guide}/ts-to-js.md (99%) rename aio/content/{cookbook => guide}/visual-studio-2015.md (99%) diff --git a/aio/content/cookbook/ajs-quick-reference.md b/aio/content/guide/ajs-quick-reference.md similarity index 99% rename from aio/content/cookbook/ajs-quick-reference.md rename to aio/content/guide/ajs-quick-reference.md index eb69ea22f4..792e332085 100644 --- a/aio/content/cookbook/ajs-quick-reference.md +++ b/aio/content/guide/ajs-quick-reference.md @@ -896,7 +896,7 @@ For more information on pipes, see [Pipes](../guide/pipes.html). Displays the collection in the order specified by the expression. - In this example, the movie title orders the movieList. + In this example, the movie title orders the `movieList`. diff --git a/aio/content/cookbook/aot-compiler.md b/aio/content/guide/aot-compiler.md similarity index 99% rename from aio/content/cookbook/aot-compiler.md rename to aio/content/guide/aot-compiler.md index 2e7c335167..f349c0f93c 100644 --- a/aio/content/cookbook/aot-compiler.md +++ b/aio/content/guide/aot-compiler.md @@ -2,7 +2,7 @@ Ahead-of-Time Compilation @intro -Learn how to use Ahead-of-time compilation +Learn how to use Ahead-of-time compilation. @description This cookbook describes how to radically improve performance by compiling _Ahead of Time_ (AOT) diff --git a/aio/content/guide/architecture.md b/aio/content/guide/architecture.md index bf8972b46c..380757a6b6 100644 --- a/aio/content/guide/architecture.md +++ b/aio/content/guide/architecture.md @@ -2,7 +2,7 @@ Architecture Overview @intro -The basic building blocks of Angular applications +The basic building blocks of Angular applications. @description You write Angular applications by composing HTML *templates* with Angularized markup, diff --git a/aio/content/guide/attribute-directives.md b/aio/content/guide/attribute-directives.md index b8a8df72d0..edf0f1624a 100644 --- a/aio/content/guide/attribute-directives.md +++ b/aio/content/guide/attribute-directives.md @@ -9,7 +9,6 @@ An **Attribute** directive changes the appearance or behavior of a DOM element. # Contents - * [Directives overview](#directive-overview) * [Build a simple attribute directive](#write-directive) * [Apply the attribute directive to an element in a template](#apply-directive) @@ -19,9 +18,6 @@ An **Attribute** directive changes the appearance or behavior of a DOM element. Try the . - - -{@a directive-overview} ## Directives overview There are three kinds of directives in Angular: @@ -38,13 +34,12 @@ Two examples are [NgFor](template-syntax.html#ngFor) and [NgIf](template-syntax. Learn about them in the [Structural Directives](structural-directives.html) guide. *Attribute directives* are used as attributes of elements. -The built-in [NgStyle](template-syntax.html#ngStyle) directive in the [Template Syntax](template-syntax.html) guide, for example, +The built-in [NgStyle](template-syntax.html#ngStyle) directive in the +[Template Syntax](template-syntax.html) guide, for example, can change several element styles at the same time. - - -{@a write-directive} ## Build a simple attribute directive + An attribute directive minimally requires building a controller class annotated with `@Directive`, which specifies the selector that identifies the attribute. @@ -53,35 +48,45 @@ The controller class implements the desired directive behavior. This page demonstrates building a simple _myHighlight_ attribute directive to set an element's background color when the user hovers over that element. You can apply it like this: - - -{@example 'attribute-directives/ts/src/app/app.component.1.html' region='applied'} - ### Write the directive code + Follow the [setup](setup.html) instructions for creating a new local project named attribute-directives. -Create the following source file in `src/app` with the following code: + +Create the following source file in the indicated folder: + {@example 'attribute-directives/ts/src/app/highlight.directive.1.ts'} +The `import` statement specifies symbols from the Angular `core`: + +1. `Directive` provides the functionality of the `@Directive` decorator. +1. `ElementRef` [injects](dependency-injection.html) into the directive's constructor +so the code can access the DOM element. +1. `Input` allows data to flow from the binding expression into the directive. + +Next, the `@Directive` decorator function contains the directive metadata in a configuration object +as an argument. `@Directive` requires a CSS selector to identify the HTML in the template that is associated with the directive. The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) is the attribute name in square brackets. Here, the directive's selector is `[myHighlight]`. Angular locates all elements in the template that have an attribute named `myHighlight`. + ### Why not call it "highlight"? + Though *highlight* is a more concise name than *myHighlight* and would work, a best practice is to prefix selector names to ensure -they don't conflict with standard HTML attributes. +they don't conflict with standard HTML attributes. This also reduces the risk of colliding with third-party directive names. Make sure you do **not** prefix the `highlight` directive name with **`ng`** because -that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. For a simple demo, the short prefix, `my`, helps distinguish your custom directive. - -

- After the @Directive metadata comes the directive's controller class, called HighlightDirective , which contains the logic for the directive. -

+that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. +For a simple demo, the short prefix, `my`, helps distinguish your custom directive. +After the `@Directive` metadata comes the directive's controller class, +called `HighlightDirective`, which contains the logic for the directive. +Exporting `HighlightDirective` makes it accessible to other components. Angular creates a new instance of the directive's controller class for each matching element, injecting an Angular `ElementRef` @@ -89,29 +94,28 @@ into the constructor. `ElementRef` is a service that grants direct access to the DOM element through its `nativeElement` property. - - -{@a apply-directive} ## Apply the attribute directive + To use the new `HighlightDirective`, create a template that applies the directive as an attribute to a paragraph (`

`) element. In Angular terms, the `

` element is the attribute **host**. -

- Put the template in its own file that looks like this: -

+Put the template in its own app.component.html +file that looks like this: {@example 'attribute-directives/ts/src/app/app.component.1.html'} Now reference this template in the `AppComponent`: + {@example 'attribute-directives/ts/src/app/app.component.ts'} Next, add an `import` statement to fetch the `Highlight` directive and add that class to the `declarations` NgModule metadata. This way Angular recognizes the directive when it encounters `myHighlight` in the template. + {@example 'attribute-directives/ts/src/app/app.module.ts'} Now when the app runs, the `myHighlight` directive highlights the paragraph text. @@ -123,8 +127,10 @@ Now when the app runs, the `myHighlight` directive highlights the paragraph text ### Your directive isn't working? -Did you remember to add the directive to the `declarations` attribute of `@NgModule`? It is easy to forget! +Did you remember to add the directive to the `declarations` attribute of `@NgModule`? +It is easy to forget! Open the console in the browser tools and look for an error like this: + EXCEPTION: Template parse errors: Can't bind to 'myHighlight' since it isn't a known property of 'p'. @@ -140,9 +146,6 @@ It created an instance of the `HighlightDirective` class and injected a reference to the `

` element into the directive's constructor which sets the `

` element's background style to yellow. - - -{@a respond-to-user} ## Respond to user-initiated events Currently, `myHighlight` simply sets an element color. @@ -152,14 +155,10 @@ and respond by setting or clearing the highlight color. Begin by adding `HostListener` to the list of imported symbols; add the `Input` symbol as well because you'll need it soon. - -{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='imports'} - -Then add two eventhandlers that respond when the mouse enters or leaves, each adorned by the `HostListener` !{_decorator}. - -{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='mouse-methods'} - -The `@HostListener` !{_decorator} lets you subscribe to events of the DOM element that hosts an attribute directive, the `

` in this case. +Then add two eventhandlers that respond when the mouse enters or leaves, +each adorned by the `HostListener` !{_decorator}. +The `@HostListener` !{_decorator} lets you subscribe to events of the DOM +element that hosts an attribute directive, the `

` in this case. Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually. There are at least three problems with _that_ approach: @@ -169,24 +168,19 @@ There are at least three problems with _that_ approach: 1. Talking to DOM API directly isn't a best practice. The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`, which you declare and initialize in the constructor. - - -{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='ctor'} - Here's the updated directive in full: + {@example 'attribute-directives/ts/src/app/highlight.directive.2.ts'} -Run the app and confirm that the background color appears when the mouse hovers over the `p` and -disappears as it moves out. +Run the app and confirm that the background color appears when +the mouse hovers over the `p` and disappears as it moves out. +

Second Highlight
- - -{@a bindings} ## Pass values into the directive with an _@Input_ data binding Currently the highlight color is hard-coded _within_ the directive. That's inflexible. @@ -194,9 +188,6 @@ In this section, you give the developer the power to set the highlight color whi Start by adding a `highlightColor` property to the directive class like this: -{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color'} - - {@a input} ### Binding to an _@Input_ property @@ -207,30 +198,15 @@ It's called an *input* property because data flows from the binding expression _ Without that input metadata, Angular rejects the binding; see [below](#why-input "Why add @Input?") for more about that. Try it by adding the following directive binding variations to the `AppComponent` template: - -{@example 'attribute-directives/ts/src/app/app.component.1.html' region='color-1'} - Add a `color` property to the `AppComponent`. - -{@example 'attribute-directives/ts/src/app/app.component.1.ts' region='class'} - Let it control the highlight color with a property binding. - -{@example 'attribute-directives/ts/src/app/app.component.1.html' region='color-2'} - That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this. - -{@example 'attribute-directives/ts/src/app/app.component.html' region='color'} - The `[myHighlight]` attribute binding both applies the highlighting directive to the `

` element and sets the directive's highlight color with a property binding. You're re-using the directive's attribute selector (`[myHighlight]`) to do both jobs. That's a crisp, compact syntax. You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name. - -{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color-2'} - This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent. @@ -244,60 +220,54 @@ _Inside_ the directive the property is known as `highlightColor`. _Outside_ the directive, where you bind to it, it's known as `myHighlight`. You get the best of both worlds: the property name you want and the binding syntax you want: - -{@example 'attribute-directives/ts/src/app/app.component.html' region='color'} - Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it. -If someone neglects to bind to `highlightColor`, highlight in "red" by default. +If someone neglects to bind to `highlightColor`, highlight in red: +Here's the latest version of the directive class. +## Write a harness to try it - -{@example 'attribute-directives/ts/src/app/highlight.directive.3.ts' region='mouse-enter'} - -Here's the latest version of the directive class.## Write a harness to try itIt may be difficult to imagine how this directive actually works. +It may be difficult to imagine how this directive actually works. In this section, you'll turn `AppComponent` into a harness that lets you pick the highlight color with a radio button and bind your color choice to the directive. -Update `app.component.html` as follows: -Revise the `AppComponent.color` so that it has no initial value.Here is the harness and directive in action. +Update app.component.html as follows: +Revise the `AppComponent.color` so that it has no initial value. +Here are the harness and directive in action. +

Highlight v.2
- - -{@a second-property} ## Bind to a second property + This highlight directive has a single customizable property. In a real app, it may need more. -At the moment, the default color—the color that prevails until -the user picks a highlight color—is hard-coded as "red". +At the moment, the default color—the color that prevails until +the user picks a highlight color—is hard-coded as "red". Let the template developer set the default color. -Add a second **input** property to `HighlightDirective` called `defaultColor`:Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`, +Add a second **input** property to `HighlightDirective` called `defaultColor`: +Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`, then with the `defaultColor`, and falls back to "red" if both properties are undefined. - -{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='mouse-enter'} - How do you bind to a second property when you're already binding to the `myHighlight` attribute name? As with components, you can add as many directive property bindings as you need by stringing them along in the template. The developer should be able to write the following template HTML to both bind to the `AppComponent.color` and fall back to "violet" as the default color. - -{@example 'attribute-directives/ts/src/app/app.component.html' region='defaultColor'} - Angular knows that the `defaultColor` binding belongs to the `HighlightDirective` because you made it _public_ with the `@Input` !{_decorator}. Here's how the harness should work when you're done coding. +
Final Highlight
## Summary + This page covered how to: + - [Build an **attribute directive**](#write-directive) that modifies the behavior of an element. - [Apply the directive](#apply-directive) to an element in a template. - [Respond to **events**](#respond-to-user) that change the directive's behavior. @@ -341,20 +311,11 @@ The final source code follows: You can also experience and download the . - -{@a why-input} - ### Appendix: Why add _@Input_? In this demo, the `hightlightColor` property is an ***input*** property of the `HighlightDirective`. You've seen it applied without an alias: - -{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color'} - You've seen it with an alias: - -{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='color'} - Either way, the `@Input` !{_decorator} tells Angular that this property is _public_ and available for binding by a parent component. Without `@Input`, Angular refuses to bind to the property. @@ -377,20 +338,17 @@ Only then can it be bound by some other component or directive. You can tell if `@Input` is needed by the position of the property name in a binding. * When it appears in the template expression to the ***right*** of the equals (=), -it belongs to the template's component and does not require the `@Input` !{_decorator}. + it belongs to the template's component and does not require the `@Input` !{_decorator}. * When it appears in **square brackets** ([ ]) to the **left** of the equals (=), -the property belongs to some _other_ component or directive; -that property must be adorned with the `@Input` !{_decorator}. + the property belongs to some _other_ component or directive; + that property must be adorned with the `@Input` !{_decorator}. Now apply that reasoning to the following example: - -{@example 'attribute-directives/ts/src/app/app.component.html' region='color'} - * The `color` property in the expression on the right belongs to the template's component. -The template and its component trust each other. -The `color` property doesn't require the `@Input` !{_decorator}. + The template and its component trust each other. + The `color` property doesn't require the `@Input` !{_decorator}. -* The `myHighlight` property on the left refers to an _aliased_ property of the `MyHighlightDirective`, -not a property of the template's component. There are trust issues. -Therefore, the directive property must carry the `@Input` !{_decorator}. \ No newline at end of file +* The `myHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`, + not a property of the template's component. There are trust issues. + Therefore, the directive property must carry the `@Input` !{_decorator}. \ No newline at end of file diff --git a/aio/content/guide/browser-support.md b/aio/content/guide/browser-support.md index b3c01ea9c4..e2bea3d79d 100644 --- a/aio/content/guide/browser-support.md +++ b/aio/content/guide/browser-support.md @@ -244,7 +244,7 @@ using SauceLabs and Angular is built on the latest standards of the web platform. Targeting such a wide range of browsers is challenging because they do not support all features of modern browsers. -You compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`) +You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`) that implement missing features in JavaScript. {@example 'quickstart/ts/src/index.html' region='polyfills'} @@ -252,12 +252,12 @@ that implement missing features in JavaScript. A particular browser may require at least one polyfill to run _any_ Angular application. You may need additional polyfills for specific features. -The tables below will help you determine which polyfills to load, depending on the browsers you target and the features you use. +The tables below can help you determine which polyfills to load, depending on the browsers you target and the features you use. ~~~ {.alert.is-important} -The suggested polyfills are the ones we know will run full Angular applications. +The suggested polyfills are the ones that run full Angular applications. You may need additional polyfills to support features not covered by this list. Note that polyfills cannot magically transform an old, slow browser into a modern, fast one. @@ -457,7 +457,7 @@ Below are the polyfills which are used to test the framework itself. They are a - Licence + License @@ -517,7 +517,7 @@ Below are the polyfills which are used to test the framework itself. They are a - MIT / Unicode licence + MIT / Unicode license @@ -611,4 +611,5 @@ Below are the polyfills which are used to test the framework itself. They are a -\* Figures are for minified and gzipped code, computed with the closure compiler \ No newline at end of file +\* Figures are for minified and gzipped code, +computed with the closure compiler. \ No newline at end of file diff --git a/aio/content/cookbook/dependency-injection.md b/aio/content/guide/cb-dependency-injection.md similarity index 99% rename from aio/content/cookbook/dependency-injection.md rename to aio/content/guide/cb-dependency-injection.md index 027395b594..ad7cf674ce 100644 --- a/aio/content/cookbook/dependency-injection.md +++ b/aio/content/guide/cb-dependency-injection.md @@ -2,7 +2,7 @@ Dependency Injection @intro -Techniques for Dependency Injection +Techniques for Dependency Injection. @description Dependency Injection is a powerful pattern for managing code dependencies. @@ -136,7 +136,7 @@ Technically, the `@Injectable()`decorator is only _required_ for a service class The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()` and the generated code would be slightly smaller. -But the service would break the moment we gave it a dependency and we'd have to go back and +But the service would break the moment we gave it a dependency and we'd have to go back and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain. @@ -362,7 +362,7 @@ Here's a typical example: {@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='ctor'} -Angular asks the injector for the service associated with the `LoggerService` and +Angular asks the injector for the service associated with the `LoggerService` and assigns the returned value to the `logger` parameter. Where did the injector get that value? diff --git a/aio/content/cookbook/index.md b/aio/content/guide/cb-index.md similarity index 99% rename from aio/content/cookbook/index.md rename to aio/content/guide/cb-index.md index 14bdfd7309..25f3d2289a 100644 --- a/aio/content/cookbook/index.md +++ b/aio/content/guide/cb-index.md @@ -2,7 +2,7 @@ Cookbook @intro -A collection of recipes for common Angular application scenarios +A collection of recipes for common Angular application scenarios. @description The *Cookbook* offers answers to common implementation questions. diff --git a/aio/content/guide/change-log.md b/aio/content/guide/change-log.md index a4beeaf54e..ffc534fd5c 100644 --- a/aio/content/guide/change-log.md +++ b/aio/content/guide/change-log.md @@ -19,12 +19,12 @@ Discusses ``. Revised samples are more clear and cover all topics discussed. ## NEW: Samples re-structured with `src/` folder (2017-02-02) -All documentation samples have been realigned with the default folder structure of the angular-cli. -That's a step along the road to basing our sample in the angular-cli. +All documentation samples have been realigned with the default folder structure of the Angular CLI. +That's a step along the road to basing the sample in the Angular CLI. But it's also good in its own right. It helps clearly separate app code from setup and configuration files. -We've updated all samples with an `src/` folder at the project root. +All samples now have a `src/` folder at the project root. The former `app/` folder moves under `src/`. Read about moving your existing project to this structure in @@ -50,24 +50,24 @@ The new [Deployment](deployment.html) guide describes techniques for putting you It includes important advice on optimizing for production. ## Hierarchical Dependency Injection: refreshed (2017-01-13) -[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) guide significantly revised. -Closes issue #3086 -Revised samples are more clear and cover all topics discussed. +[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) guide is significantly revised. +Closes issue #3086. +Revised samples are clearer and cover all topics discussed. ## Miscellaneous (2017-01-05) * [Setup](setup.html) guide: added (optional) instructions on how to remove _non-essential_ files. * No longer consolidate RxJS operator imports in `rxjs-extensions` file; each file should import what it needs. -* All samples prepend template/style URLS URLs w/ `./` ... and so should you. +* All samples prepend template/style URLs with `./` as a best practice. * [Style Guide](style-guide.html): copy edits and revised rules. ## Router: more detail (2016-12-21) Added more information to the [Router](router.html) guide including sections named outlets, wildcard routes, and preload strategies. -## Http: how to set default request headers (and other request options) (2016-12-14) +## HTTP: how to set default request headers (and other request options) (2016-12-14) Added section on how to set default request headers (and other request options) to -[Http](server-communication.html#override-default-request-options) guide. +[HTTP](server-communication.html#override-default-request-options) guide. ## Testing: added component test plunkers (2016-12-02) Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: one for the QuickStart seed's `AppComponent` and another for the Testing guide's `BannerComponent`. @@ -79,9 +79,9 @@ translation of alternative texts with `select`. The sample demonstrates these features too. ## Testing: karma file updates (2016-11-30) -* karma.config + karma-test-shim can handle multiple spec source paths; -see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294) -* Displays Jasmine Runner output in the karma-launched browser +* `karma.config` + `karma-test-shim` can handle multiple spec source paths; +see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294). +* Displays Jasmine Runner output in the karma-launched browser. ## QuickStart Rewrite (2016-11-18) The QuickStart is completely rewritten so that it actually is quick. @@ -91,7 +91,7 @@ by downloading (or cloning) the QuickStart github repository. You are no longer asked to copy-and-paste code into setup files that were not explained anyway. ## Sync with Angular v.2.2.0 (2016-11-14) -Docs and code samples updated and tested with Angular v.2.2.0 +Docs and code samples updated and tested with Angular v.2.2.0. ## UPDATE: NgUpgrade Guide for the AOT friendly _upgrade/static_ module (2016-11-14) The updated [NgUpgrade Guide](upgrade.html) guide covers the @@ -101,20 +101,20 @@ facility for migrating from AngularJS to Angular. The documentation for the version prior to v.2.2.0 has been removed. ## ES6 described in "TypeScript to JavaScript" (2016-11-14) -The updated "[TypeScript to JavaScript](../cookbook/ts-to-js.html)" cookbook +The updated [TypeScript to JavaScript](../cookbook/ts-to-js.html) cookbook now explains how to write apps in ES6/7 by translating the common idioms in the TypeScript documentation examples (and elsewhere on the web) to ES6/7 and ES5. ## Sync with Angular v.2.1.1 (2016-10-21) -Docs and code samples updated and tested with Angular v.2.1.0 +Docs and code samples updated and tested with Angular v.2.1.1. ## npm _@types_ packages replace _typings_ (2016-10-20) Documentation samples now get TypeScript type information for 3rd party libraries from npm `@types` packages rather than with the _typings_ tooling. The `typings.json` file is gone. -The "[AngularJS Upgrade](upgrade.html)" guide reflects this change. +The [AngularJS Upgrade](upgrade.html) guide reflects this change. The `package.json` installs `@types/angular` and several `@types/angular-...` packages in support of upgrade; these are not needed for pure Angular development. @@ -135,7 +135,7 @@ _before_ the user navigates to them for improved perceived performance. New `:enter` and `:leave` aliases make animation more natural. ## Sync with Angular v.2.1.0 (2016-10-12) -Docs and code samples updated and tested with Angular v.2.1.0 +Docs and code samples updated and tested with Angular v.2.1.0. ## NEW "Ahead of time (AOT) Compilation" cookbook (2016-10-11) The NEW [Ahead of time (AOT) Compilation](../cookbook/aot-compiler.html) cookbook @@ -144,7 +144,7 @@ It demonstrates the basics with a QuickStart app followed by the more advanced considerations of compiling and bundling the Tour of Heroes. ## Sync with Angular v.2.0.2 (2016-10-6) -Docs and code samples updated and tested with Angular v.2.0.2 +Docs and code samples updated and tested with Angular v.2.0.2. ## "Routing and Navigation" guide with the _Router Module_ (2016-10-5) The [Routing and Navigation](router.html) guide now locates route configuration @@ -171,16 +171,16 @@ The new "angular-in-memory-web-api" has new features. ## "Style Guide" with _NgModules_ (2016-09-27) -[StyleGuide](style-guide.html) explains our recommended conventions for Angular modules (NgModule). +[StyleGuide](style-guide.html) explains recommended conventions for Angular modules (NgModule). Barrels now are far less useful and have been removed from the style guide; they remain valuable but are not a matter of Angular style. -We also relaxed the rule that discouraged use of the `@Component.host` property. +Also relaxed the rule that discouraged use of the `@Component.host` property. ## _moduleId: module.id_ everywhere (2016-09-25) Sample components that get their templates or styles with `templateUrl` or `styleUrls` have been converted to _module-relative_ URLs. -We added the `moduleId: module.id` property-and-value to their `@Component` metadata. +Added the `moduleId: module.id` property-and-value to their `@Component` metadata. This change is a requirement for compilation with AOT compiler when the app loads modules with SystemJS as the samples currently do. diff --git a/aio/content/cookbook/component-communication.md b/aio/content/guide/component-communication.md similarity index 99% rename from aio/content/cookbook/component-communication.md rename to aio/content/guide/component-communication.md index 4fd6c0a95d..6b887c9b56 100644 --- a/aio/content/cookbook/component-communication.md +++ b/aio/content/guide/component-communication.md @@ -2,7 +2,7 @@ Component Interaction @intro -Share information between different directives and components +Share information between different directives and components. @description This cookbook contains recipes for common component communication scenarios @@ -118,7 +118,7 @@ the expected `ngOnChanges` calls and values: ## Parent listens for child event -The child component exposes an `EventEmitter` property with which it `emits`events when something happens. +The child component exposes an `EventEmitter` property with which it `emits` events when something happens. The parent binds to that event property and reacts to those events. The child's `EventEmitter` property is an ***output property***, diff --git a/aio/content/cookbook/component-relative-paths.md b/aio/content/guide/component-relative-paths.md similarity index 100% rename from aio/content/cookbook/component-relative-paths.md rename to aio/content/guide/component-relative-paths.md diff --git a/aio/content/guide/displaying-data.md b/aio/content/guide/displaying-data.md index 999d955ac9..ae30fe7a01 100644 --- a/aio/content/guide/displaying-data.md +++ b/aio/content/guide/displaying-data.md @@ -100,7 +100,7 @@ For more information, see the [Template Syntax](./template-syntax.html#ngFor) pa Notice the `hero` in the `ngFor` double-quoted instruction; it is an example of a template input variable. Read -more about template input variables in the [microsyntax](./template-syntax.html#ngForMicrosyntax) section of +more about template input variables in the [microsyntax](./template-syntax.html#microsyntax) section of the [Template Syntax](./template-syntax.html) page. Angular duplicates the `
  • ` for each item in the list, setting the `hero` variable diff --git a/aio/content/cookbook/dynamic-component-loader.md b/aio/content/guide/dynamic-component-loader.md similarity index 99% rename from aio/content/cookbook/dynamic-component-loader.md rename to aio/content/guide/dynamic-component-loader.md index c232412f91..844698f156 100644 --- a/aio/content/cookbook/dynamic-component-loader.md +++ b/aio/content/guide/dynamic-component-loader.md @@ -2,7 +2,7 @@ Dynamic Component Loader @intro -Load components dynamically +Load components dynamically. @description Component templates are not always fixed. An application may need to load new components at runtime. diff --git a/aio/content/cookbook/dynamic-form.md b/aio/content/guide/dynamic-form.md similarity index 97% rename from aio/content/cookbook/dynamic-form.md rename to aio/content/guide/dynamic-form.md index 0ca0e144d1..87815ddff5 100644 --- a/aio/content/cookbook/dynamic-form.md +++ b/aio/content/guide/dynamic-form.md @@ -2,7 +2,7 @@ Dynamic Forms @intro -Render dynamic forms with FormGroup +Render dynamic forms with FormGroup. @description We can't always justify the cost and time to build handcrafted forms, @@ -129,7 +129,7 @@ The `ngSwitch` determines which type of question to display. In both components we're relying on Angular's **formGroup** to connect the template HTML to the underlying control objects, populated from the question model with display and validation rules. -`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`. +`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`. ## Questionnaire data`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`. The set of questions we have defined for the job application is returned from the `QuestionService`. diff --git a/aio/content/cookbook/form-validation.md b/aio/content/guide/form-validation.md similarity index 98% rename from aio/content/cookbook/form-validation.md rename to aio/content/guide/form-validation.md index 2fdb7559e7..16cef1695a 100644 --- a/aio/content/cookbook/form-validation.md +++ b/aio/content/guide/form-validation.md @@ -2,7 +2,7 @@ Form Validation @intro -Validate user's form entries +Validate user's form entries. @description @@ -258,7 +258,7 @@ At runtime, Angular interprets the template and derives its _form control model_ **Reactive Forms** takes a different approach. You create the form control model in code. You write the template with form elements -and`form...` directives from the Angular `ReactiveFormsModule`. +and `form...` directives from the Angular `ReactiveFormsModule`. At runtime, Angular binds the template elements to your control model based on your instructions. This approach requires a bit more effort. *You have to write the control model and manage it*. @@ -365,7 +365,7 @@ Angular has stock validators that correspond to the standard HTML validation att The `forbiddenNames` validator on the `"name"` control is a custom validator, discussed in a separate [section below](#custom-validation). - Learn more about `FormBuilder` in a _forthcoming_ chapter on reactive forms. + Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide. #### Committing hero value changes In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. diff --git a/aio/content/guide/glossary.md b/aio/content/guide/glossary.md index d21fcde906..404ce21a90 100644 --- a/aio/content/guide/glossary.md +++ b/aio/content/guide/glossary.md @@ -2,5 +2,5 @@ Glossary @intro -Brief definitions of the most important words in the Angular vocabulary +Brief definitions of the most important words in the Angular vocabulary. diff --git a/aio/content/guide/hierarchical-dependency-injection.md b/aio/content/guide/hierarchical-dependency-injection.md index 6e5b592819..6441683b5b 100644 --- a/aio/content/guide/hierarchical-dependency-injection.md +++ b/aio/content/guide/hierarchical-dependency-injection.md @@ -41,7 +41,7 @@ open simultaneously. injector tree -### Injector bubbling +### Injector bubbling When a component requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector. If the component's injector lacks the provider, it passes the request up to its parent component's injector. @@ -51,22 +51,24 @@ If it runs out of ancestors, Angular throws an error. You can cap the bubbling. An intermediate component can declare that it is the "host" component. The hunt for providers will climb no higher than the injector for that host component. -This a topic for another day. +This is a topic for another day. ### Re-providing a service at different levels + You can re-register a provider for a particular dependency token at multiple levels of the injector tree. -You don't *have* to re-register providers. You shouldn't do so unless you have a good reason. +You don't *have* to re-register providers. You shouldn't do so unless you have a good reason. But you *can*. As the resolution logic works upwards, the first provider encountered wins. Thus, a provider in an intermediate injector intercepts a request for a service from something lower in the tree. It effectively "reconfigures" and "shadows" a provider at a higher level in the tree. -If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat. +If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat. All requests bubble up to the root NgModule injector that you configured with the `!{_bootstrapModule}` method. ## Component injectors The ability to configure one or more providers at different levels opens up interesting and useful possibilities. + ### Scenario: service isolation Architectural reasons may lead you to restrict access to a service to the application domain where it belongs. @@ -74,57 +76,55 @@ Architectural reasons may lead you to restrict access to a service to the applic The guide sample includes a `VillainsListComponent` that displays a list of villains. It gets those villains from a `VillainsService`. -While you could provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`), +While you _could_ provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`), that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows. -If you later modify the `VillainsService`, you could break something in a hero component somewhere. -That's not supposed to happen but the way you've provided the service creates that risk. +If you later modified the `VillainsService`, you could break something in a hero component somewhere. +That's not supposed to happen but providing the service in the root `AppModule` creates that risk. Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this: - - -{@example 'hierarchical-dependency-injection/ts/src/app/villains-list.component.ts' region='metadata'} - -By providing `VillainsService` in the `VillainsListComponent` metadata — and nowhere else —, +By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else, the service becomes available only in the `VillainsListComponent` and its sub-component tree. It's still a singleton, but it's a singleton that exist solely in the _villain_ domain. -You are confident that a hero component can't access it. You've reduced your exposure to error. +Now you know that a hero component can't access it. You've reduced your exposure to error. ### Scenario: multiple edit sessions Many applications allow users to work on several open tasks at the same time. -For example, in a tax preparation application, the preparer could be working several tax returns, +For example, in a tax preparation application, the preparer could be working on several tax returns, switching from one to the other throughout the day. This guide demonstrates that scenario with an example in the Tour of Heroes theme. Imagine an outer `HeroListComponent` that displays a list of super heroes. -To open a hero's tax return, the preparer clicks on a hero name, which opens a component for editing that return. +To open a hero's tax return, the preparer clicks on a hero name, which opens a component for editing that return. Each selected hero tax return opens in its own component and multiple returns can be open at the same time. -Each tax return component -* is its own tax return editing session. -* can change a tax return without affecting a return in another component. -* has the ability to save the changes to its tax return or cancel them. +Each tax return component has the following characteristics: +* Is its own tax return editing session. +* Can change a tax return without affecting a return in another component. +* Has the ability to save the changes to its tax return or cancel them.
    Heroes in action
    -One might suppose that the `TaxReturnComponent` has logic to manage and restore changes. -That would be a pretty easy task for a simple hero tax return. +One might suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes. +That would be a pretty easy task for a simple hero tax return. In the real world, with a rich tax return data model, the change management would be tricky. You might delegate that management to a helper service, as this example does. -Here is the `HeroTaxReturnService`. +Here is the `HeroTaxReturnService`. It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it. -It also delegates to the application-wide, singleton `HeroService`, which it gets by injection. +It also delegates to the application-wide singleton `HeroService`, which it gets by injection. + {@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts'} Here is the `HeroTaxReturnComponent` that makes use of it. + {@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts'} The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters. @@ -133,29 +133,25 @@ The getter always returns what that service says is the current state of the her The component also asks the service to save and restore this tax return. There'd be big trouble if _this_ service were an application-wide singleton. -Every component would share the same service instance. +Every component would share the same service instance. Each component would overwrite the tax return that belonged to another hero. What a mess! Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property. - - -{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts' region='providers'} - -The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`. -Recall that every component _instance_ has its own injector. +The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`. +Recall that every component _instance_ has its own injector. Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service. No tax return overwriting. No mess. The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation. -You can review it and download it from the +You can review it and download it from the . ### Scenario: specialized providers Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service, deeper in the component tree. Consider again the Car example from the [Dependency Injection](./dependency-injection.html) guide. -Suppose you configured the root injector (marked as A) with _generic_ providers for +Suppose you configured the root injector (marked as A) with _generic_ providers for `CarService`, `EngineService` and `TiresService`. You create a car component (A) that displays a car constructed from these three generic services. @@ -181,4 +177,4 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample -which you can review and download from the \ No newline at end of file +which you can review and download from the . \ No newline at end of file diff --git a/aio/content/cookbook/i18n.md b/aio/content/guide/i18n.md similarity index 100% rename from aio/content/cookbook/i18n.md rename to aio/content/guide/i18n.md diff --git a/aio/content/guide/index.md b/aio/content/guide/index.md index 8abba79d27..6d608995ca 100644 --- a/aio/content/guide/index.md +++ b/aio/content/guide/index.md @@ -2,7 +2,7 @@ Documentation Overview @intro -How to read and use this documentation +How to read and use this documentation. @description This page describes the Angular documentation at a high level. diff --git a/aio/content/guide/learning-angular.md b/aio/content/guide/learning-angular.md index a96a1b1a7f..5016fbb659 100644 --- a/aio/content/guide/learning-angular.md +++ b/aio/content/guide/learning-angular.md @@ -2,7 +2,7 @@ Learning Angular @intro -A suggested path through the documentation for Angular newcomers +A suggested path through the documentation for Angular newcomers. @description diff --git a/aio/content/guide/npm-packages.md b/aio/content/guide/npm-packages.md index 804f842326..f43ccf7e39 100644 --- a/aio/content/guide/npm-packages.md +++ b/aio/content/guide/npm-packages.md @@ -2,12 +2,12 @@ Npm Packages @intro -Recommended npm packages, and how to specify package dependencies +Recommended npm packages, and how to specify package dependencies. @description Angular applications and Angular itself depend upon features and functionality provided by a variety of third-party packages. These packages are maintained and installed with the Node Package Manager (npm). -Node.js and npm are essential to Angular development. +Node.js and npm are essential to Angular development. Get them now if they're not already installed on your machine. @@ -16,23 +16,28 @@ Get them now if they're not already installed on your machine. by running the commands `node -v` and `npm -v` in a terminal/console window. Older versions produce errors. -We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if you already have projects running on your machine that use other versions of node and npm. -We recommend a comprehensive starter-set of packages as specified in the `dependencies` and `devDependencies` -sections of the package.json file -installed as described during [Setup](setup.html).You can use other packages but we recommend *this particular set* to start with because (a) they work well together and -(b) they include everything you'll need to build and run the sample applications in this series. -Note: A cookbook or guide page may require an additional library such as *jQuery*.You'll install more than you need for QuickStart. -No worries! +Consider using [nvm](https://github.com/creationix/nvm) for managing multiple +versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if +you already have projects running on your machine that use other versions of node and npm. +During [Setup](setup.html), a package.json +file is installed with a comprehensive starter set of +packages as specified in the `dependencies` and `devDependencies` sections. + +You can use other packages but the packages in _this particular set_ work well together and include +everything you need to build and run the sample applications in this series. + +Note: A cookbook or guide page may require an additional library such as *jQuery*.You'll install more than you need for the QuickStart guide. +No worries! You only serve to the client those packages that the application actually requests. This page explains what each package does. You can make substitutions later to suit your tastes and experience. ## *dependencies* and *devDependencies* -The `package.json` includes two sets of packages, +The `package.json` includes two sets of packages, [dependencies](#dependencies) and [devDependencies](#dev-dependencies). -The *dependencies* are essential to *running* the application. -The *devDependencies* are only necessary to *develop* the application. +The *dependencies* are essential to *running* the application. +The *devDependencies* are only necessary to *develop* the application. You can exclude them from production installations by adding `--production` to the install command, as follows: npm install my-application --production @@ -46,37 +51,40 @@ You can exclude them from production installations by adding `--production` to t ## *dependencies* The `dependencies` section of `package.json` contains: -* ***Features*** - Feature packages give the application framework and utility capabilities. +* ***Features***: Feature packages give the application framework and utility capabilities. -* ***Polyfills*** - Polyfills plug gaps in the browser's JavaScript implementation. +* ***Polyfills***: Polyfills plug gaps in the browser's JavaScript implementation. -* ***Other*** - Other libraries that support the application such as `bootstrap` for HTML widgets and styling. +* ***Other***: Other libraries that support the application such as `bootstrap` for HTML widgets and styling. ### Feature Packages -***@angular/core*** - Critical runtime parts of the framework needed by every application. +***@angular/core***: Critical runtime parts of the framework needed by every application. Includes all metadata decorators, `Component`, `Directive`, dependency injection, and the component lifecycle hooks. -***@angular/common*** - The commonly needed services, pipes, and directives provided by the Angular team. +***@angular/common***: The commonly needed services, pipes, and directives provided by the Angular team. -***@angular/compiler*** - Angular's *Template Compiler*. -It understands templates and can convert them to code that makes the application run and render. +***@angular/compiler***: Angular's *Template Compiler*. +It understands templates and can convert them to code that makes the application run and render. Typically you don’t interact with the compiler directly; rather, you use it indirectly via `platform-browser-dynamic` or the offline template compiler. -***@angular/platform-browser*** - Everything DOM and browser related, especially the pieces that help render into DOM. -This package also includes the bootstrapStatic method for bootstrapping applications for production builds that pre-compile templates offline. +***@angular/platform-browser***: Everything DOM and browser related, especially +the pieces that help render into the DOM. +This package also includes the `bootstrapStatic()` method +for bootstrapping applications for production builds that pre-compile templates offline. -***@angular/platform-browser-dynamic*** - Includes [Providers](../api/core/index/Provider-type-alias.html) and a [bootstrap](ngmodule.html#bootstrap) method for applications that +***@angular/platform-browser-dynamic***: Includes [Providers](../api/core/index/Provider-type-alias.html) +and a [bootstrap](ngmodule.html#bootstrap) method for applications that compile templates on the client. Don’t use offline compilation. Use this package for bootstrapping during development and for bootstrapping plunker samples. -***@angular/http*** - Angular's http client. +***@angular/http***: Angular's HTTP client. -***@angular/router*** - Component router. +***@angular/router***: Component router. -***@angular/upgrade*** - Set of utilities for upgrading AngularJS applications to Angular. +***@angular/upgrade***: Set of utilities for upgrading AngularJS applications to Angular. -***[system.js](https://github.com/systemjs/systemjs)*** - A dynamic module loader compatible with the +***[system.js](https://github.com/systemjs/systemjs)***: A dynamic module loader compatible with the [ES2015 module](http://www.2ality.com/2014/09/es6-modules-final.html) specification. Other viable choices include the well-regarded [webpack](https://webpack.github.io/). @@ -94,18 +102,18 @@ Install these polyfills using the npm packages that Angular lists in the *peerDe You must list these packages in the `dependencies` section of your own `package.json`. -For background on this requirement, see [Why peerDependencies?](#why-peer-dependencies).***core-js*** - Patches the global context (window) with essential features of ES2015 (ES6). - You may substitute an alternative polyfill that provides the same core APIs. +For background on this requirement, see [Why peerDependencies?](#why-peer-dependencies).***core-js***: Patches the global context (window) with essential features of ES2015 (ES6). + You may substitute an alternative polyfill that provides the same core APIs. When these APIs are implemented by the major browsers, this dependency will become unnecessary. -***rxjs*** - A polyfill for the [Observables specification](https://github.com/zenparsing/es-observable) currently before the +***rxjs***: A polyfill for the [Observables specification](https://github.com/zenparsing/es-observable) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language. -You can pick a preferred version of *rxjs* (within a compatible version range) +You can pick a preferred version of *rxjs* (within a compatible version range) without waiting for Angular updates. -***zone.js*** - A polyfill for the [Zone specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the +***zone.js***: A polyfill for the [Zone specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language. -You can pick a preferred version of *zone.js* to use (within a compatible version range) +You can pick a preferred version of *zone.js* to use (within a compatible version range) without waiting for Angular updates. @@ -113,12 +121,12 @@ without waiting for Angular updates. ### Other helper libraries -***angular-in-memory-web-api*** - An Angular-supported library that simulates a remote server's web api -without requiring an actual server or real http calls. -Good for demos, samples, and early stage development (before we even have a server). -Read about it in the [Http Client](server-communication.html#appendix-tour-of-heroes-in-memory-server) page. +***angular-in-memory-web-api***: An Angular-supported library that simulates a remote server's web api +without requiring an actual server or real HTTP calls. +Good for demos, samples, and early stage development (before you even have a server). +Read about it in the [HTTP Client](server-communication.html#in-mem-web-api) page. -***bootstrap*** - [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps. +***bootstrap***: [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps. Some of the samples improve their appearance with *bootstrap*. @@ -128,39 +136,38 @@ Some of the samples improve their appearance with *bootstrap*. The packages listed in the *devDependencies* section of the `package.json` help you develop the application. You don't have to deploy them with the production application although there is no harm in doing so. -***[concurrently](https://www.npmjs.com/package/concurrently)*** - +***[concurrently](https://www.npmjs.com/package/concurrently)***: A utility to run multiple *npm* commands concurrently on OS/X, Windows, and Linux operating systems. -***[lite-server](https://www.npmjs.com/package/lite-server)*** - -A light-weight, static file server, by [John Papa](http://johnpapa.net/) +***[lite-server](https://www.npmjs.com/package/lite-server)***: +A light-weight, static file server, by [John Papa](http://johnpapa.net/) with excellent support for Angular apps that use routing. -***[typescript](https://www.npmjs.com/package/typescript)*** - +***[typescript](https://www.npmjs.com/package/typescript)***: the TypeScript language server, including the *tsc* TypeScript compiler. -***@types/\**** - TypeScript definition files. -Learn more about it in the [TypeScript Configuration](typescript-configuration.html#typings) chapter. +***@types/\****: TypeScript definition files. +Learn more about it in the [TypeScript Configuration](typescript-configuration.html#typings) guide. {@a why-peer-dependencies} ## Why *peerDependencies*? -There isn't a *peerDependencies* section in the QuickStart `package.json`. -But Angular has a *peerDependencies* section in -*its* package.json, which has important consequences for your application. +There isn't a [*peerDependencies*](https://nodejs.org/en/blog/npm/peer-dependencies/) section in the QuickStart `package.json`. +But Angular has a *peerDependencies* section in +*its* `package.json`, which has important consequences for your application. -It explains why you load the [polyfill](#polyfills) *dependency* packages in the QuickStart `package.json`, +This section explains why you load the [polyfill](#polyfills) *dependency* +packages in the QuickStart application's `package.json`, and why you'll need those packages in your own applications. -An explanation of [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies/) follows. - Packages depend on other packages. For example, your application depends on the Angular package. -Two packages, "A" and "B", could depend on the same third package "C". +Two packages, "A" and "B", could depend on the same third package "C". "A" and "B" might both list "C" among their *dependencies*. -What if "A" and "B" depend on different versions of "C" ("C1" and "C2"). The npm package system supports that. +What if "A" and "B" depend on different versions of "C" ("C1" and "C2"). The npm package system supports that. It installs "C1" in the `node_modules` folder for "A" and "C2" in the `node_modules` folder for "B". Now "A" and "B" have their own copies of "C" and they run without interferring with one another. @@ -174,11 +181,11 @@ The difference between a `dependency` and a `peerDependency` is roughly this: >A **dependency** says, "I need this thing directly available to *me*." > >A **peerDependency** says, "If you want to use me, you need this thing available to *you*." - -The Angular `package.json` specifies several *peer dependency* packages, + +The Angular `package.json` specifies several *peer dependency* packages, each pinned to a particular version of a third-party package. -### We must install Angular's *peerDependencies* ourselves. +### You must install Angular's *peerDependencies* yourself. When *npm* installs packages listed in *your* `dependencies` section, it also installs the packages listed within *their* packages `dependencies` sections. @@ -191,7 +198,7 @@ the packages listed in Angular's *peerDependencies* section**. Fortunately, *npm* issues a warning (a) When any *peer dependencies* are missing, or (b) When the application or any of its other dependencies -installs a different version of a *peer dependency*. +installs a different version of a *peer dependency*. These warnings guard against accidental failures due to version mismatches. They leave you in control of package and version resolution. @@ -202,5 +209,5 @@ It is your responsibility to list all *peer dependency* packages **among your ow The Angular polyfill dependencies are hard requirements. Currently, there is no way to make them optional. -However, there is an npm feature request for "optional peerDependencies," which would allow you to model this relationship better. +However, there is an npm feature request for "optional peerDependencies," which would allow you to model this relationship better. When this feature request is implemented, Angular will switch from *peerDependencies* to *optionalPeerDependencies* for all polyfills. \ No newline at end of file diff --git a/aio/content/guide/router.md b/aio/content/guide/router.md index 89620b61a1..78cefe4aaa 100644 --- a/aio/content/guide/router.md +++ b/aio/content/guide/router.md @@ -28,49 +28,102 @@ the appropriate application view when the user clicks a link. You can navigate imperatively when the user clicks a button, selects from a drop box, or in response to some other stimulus from any source. And the router logs activity in the browser's history journal so the back and forward buttons work as well. +# Contents -You'll learn many router details in this guide which covers +* [The Basics](#basics) + - [``](#basics-base-href) + - [Router imports](#basics-router-imports) + - [Configuration](#basics-config) + - [Router outlet](#basics-router-outlet) + - [Router links](#basics-router-links) + - [Router state](#basics-router-state) + - [Summary](#basics-summary) +* [The sample application](#sample-app-intro) +* [Milestone 1: Getting started with the router](#getting-started) + - [Setting the base href](#base-href) + - [Importing from the router library](#import) + - [Define routes](#route-config) + - [The `AppComponent` shell](#shell) + - [RouterOutlet](#router-outlet) + - [`RouterLink binding`](#router-link) + - [`RouterLinkActive` binding](#router-link-active) + - [Wildcard route](#wildcard) + - [The default route to heroes](#default-route) +* [Milestone 2: Routing module](#routing-module) + - [Refactor the routing configuration into a routing module](#routing-refactor) + - [Do you need a Routing Module?](#why-routing-module) +* [Milestone 3: Heroes feature](#heroes-feature) + - [Add heroes functionality](#heroes-functionality) + - [Hero feature routing requirements](#hero-routing-requirements) + - [Hero feature route configuration](#hero-routing-module) + - [Add the routing module to the _HeroesModule_](#adding-routing-module) + - [Remove duplicate hero routes](#remove-duplicate-hero-routes) + - [Import hero module into AppModule](#merge-hero-routes) + - [Module import order matters](#routing-module-order) + - [Route Definition with a parameter](#route-def-with-parameter) + - [Navigate to hero detail imperatively](#navigate) + - [Setting the route parameters in the list view](#route-parameters) + - [ActivatedRoute: the one-stop-shop for route information](#activated-route) + - [Observable params and component reuse](#reuse) + - [Snapshot: the _no-observable_ alternative](#snapshot) + - [Navigating back to the list component](#nav-to-list) + - [Route Parameters: Required or optional?](#optional-route-parameters) + - [Heroes list: optionally selecting a hero](#optionally-selecting) + - [Route parameters in the *ActivatedRoute* service](#route-parameters-activated-route) + - [Adding animations to the routed component](#route-animation) + - [Milestone 3 wrap up](#milestone-3-wrap-up) +* [Milestone 4: Crisis center feature](#milestone-4) + - [A crisis center with child routes](#crisis-child-routes) + - [Child routing component](#child-routing-component) + - [Child route configuration](#child-route-config) + - [Import crisis center module into the _AppModule_ routes](#import-crisis-module) + - [Relative navigation](#relative-navigation) + - [Navigate to crisis detail with a relative URL](#nav-to-crisis) + - [Displaying multiple routes in named outlets](#named-outlets) + - [Secondary routes](#secondary-routes) + - [Add a secondary route](#add-secondary-route) + - [Secondary route navigation: merging routes during navigation](#secondary-route-navigation) + - [Clearing secondary routes](#clear-secondary-routes) +* [Milestone 5: Route guards](#guards) + - [`CanActivate`: requiring authentication](#can-activate-guard) + - [Component-less route: grouping routes without a component](#component-less-route) + - [Guard the admin feature](#guard-admin-feature) + - [Teach *AuthGuard* to authenticate](#teach-auth) + - [Add the login component](#add-login-component) + - [`CanActivateChild`: guarding child routes](#can-activate-child-guard) + - [`CanDeactivate`: handling unsaved changes](#can-deactivate-guard) + - [Cancel and save](#cancel-save) + - [`Resolve`: pre-fetching component data](#resolve-guard) + - [Fetch data before navigating](#fetch-before-navigating ) + - [Query parameters and fragments](#query-parameters) +* [Milestone 6: Asynchronous routing](#asynchronous-routing) + - [Lazy loading route configuration](#lazy-loading-route-config) + - [CanLoad Guard: guarding unauthorized loading of feature modules](#can-load-guard) + - [Preloading: background loading of feature areas](#preloading) + - [How preloading works](#how-preloading) + - [Lazy load the crisis center](#lazy-load-crisis-center) + - [_CanLoad_ blocks preload](#preload-canload) + - [Custom Preloading Strategy](#custom-preloading) + - [Inspect the router's configuration](#inspect-config) + - [Wrap up and final app](#final-app) +* [Appendices](#appendices) + - [Appendix: link parameters array](#link-parameters-array) + - [Appendix: *LocationStrategy* and browser URL styles](#location-strategy) -* Setting the [base href](#base-href) -* Importing from the [router library](#import) -* [Configuring the router](#route-config) -* Handling unmatched URLs with a [wildcard route](#wildcard-route) -* The [link parameters array](#link-parameters-array) that propels router navigation -* Setting the [default route](#default-route) where the application navigates at launch -* [Redirecting](#redirect) from one route to another -* Navigating when the user clicks a data-bound [RouterLink](#router-link) -* Navigating under [program control](#navigate) -* Retrieving information from the [route](#activated-route) -* [Animating](#route-animation) transitions for route components -* Navigating [relative](#relative-navigation) to the current URL -* Toggling css classes for the [active router link](#router-link-active) -* Embedding critical information in the URL with [route parameters](#route-parameters) -* Providing non-critical information in [optional route parameters](#optional-route-parameters) -* Refactoring routing into a [routing module](#routing-module) -* [Importing routing modules in the proper order](#routing-module-order) -* Add [child routes](#child-routing-component) under a feature section -* [Grouping child routes](#component-less-route) without a component -* Displaying [multiple routes](#named-outlets) in separate outlets -* Confirming or canceling navigation with [guards](#guards) - * [CanActivate](#can-activate-guard) to prevent navigation to a route - * [CanActivateChild](#can-activate-child-guard) to prevent navigation to a child route - * [CanDeactivate](#can-deactivate-guard) to prevent navigation away from the current route - * [Resolve](#resolve-guard) to pre-fetch data before activating a route - * [CanLoad](#can-load-guard) to prevent asynchronous routing -* Providing optional information across routes with [query parameters](#query-parameters) -* Jumping to anchor elements using a [fragment](#fragment) -* Loading feature areas [asynchronously](#asynchronous-routing) -* Preloading feature areas [during navigation](#preloading) -* Using a [custom strategy](#custom-preloading) to only preload certain features -* [Inspect the router's configuration](#inspect-config). -* Choosing the "HTML5" or "hash" [URL style](#browser-url-styles) + + + +{@a basics} ## The Basics This guide proceeds in phases, marked by milestones, starting from a simple two-pager and building toward a modular, multi-view design with child routes. An introduction to a few core router concepts will help orient you to the details that follow. + + +{@a basics-base-href} ### *<base href>* Most routing applications should add a `` element to the `index.html` as the first child in the `` tag @@ -78,15 +131,22 @@ to tell the router how to compose navigation URLs. If the `app` folder is the application root, as it is for the sample application, set the `href` value *exactly* as shown here. + + +{@a basics-router-imports} ### Router imports The Angular Router is an optional service that presents a particular component view for a given URL. It is not part of the Angular core. It is in its own library package, `@angular/router`. Import what you need from it as you would from any other Angular package. -You'll learn about more options in the [details below](#browser-url-styles).### Configuration +You'll learn about more options in the [details below](#browser-url-styles). -A routed Angular application has one, singleton instance of the *`Router`* service. + +{@a basics-config} +### Configuration + +A routed Angular application has one singleton instance of the *`Router`* service. When the browser's URL changes, that router looks for a corresponding `Route` from which it can determine the component to display. @@ -102,7 +162,7 @@ Pass it to the `RouterModule.forRoot` method in the module `imports` to configur Each `Route` maps a URL `path` to a component. There are _no leading slashes_ in the _path_. The router parses and builds the final URL for you, -allowing you to use both relative and "absolute" paths when navigating between application views. +allowing you to use both relative and absolute paths when navigating between application views. The `:id` in the first 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` @@ -127,6 +187,9 @@ strategy when matching routes, so more specific routes should be placed above le In the configuration above, routes with a static path are listed first, followed by an empty path route, that matches the default route. The wildcard route comes last because it matches _every URL_ and should be selected _only_ if no other routes are matched first. + + +{@a basics-router-outlet} ### Router outlet Given this configuration, when the browser URL for this application becomes `/heroes`, @@ -139,6 +202,9 @@ _after_ a `RouterOutlet` that you've placed in the host view's HTML. + + +{@a basics-router-links} ### Router links Now you have routes configured and a place to render them, but @@ -157,6 +223,9 @@ The router resolves that array into a complete URL. The **`RouterLinkActive`** directive on each anchor tag helps visually distinguish the anchor for the currently selected "active" route. The router adds the `active` CSS class to the element when the associated *RouterLink* becomes active. You can add this directive to the anchor or to its parent element. + + +{@a basics-router-state} ### Router state After the end of each successful navigation lifecycle, the router builds a tree of `ActivatedRoute` objects @@ -165,6 +234,9 @@ application using the `Router` service and the `routerState` property. Each `ActivatedRoute` in the `RouterState` provides methods to traverse up and down the route tree to get information from parent, child and sibling routes. + + +{@a basics-summary} ### Summary The application has a configured router. @@ -265,7 +337,7 @@ Here are the key `Router` terms and their meanings: - The directive (<router-outlet>) that marks where the router should display a view. + The directive (<router-outlet>) that marks where the router displays a view. @@ -298,7 +370,7 @@ Here are the key `Router` terms and their meanings: The directive for adding/removing classes from an HTML element when an associated - routerLink contained on or inside the element becomes active/inactive. + routerLink contained on or inside the element becomes active/inactive. @@ -314,7 +386,7 @@ Here are the key `Router` terms and their meanings: A service that is provided to each route component that contains route specific - information such as route parameters, static data, resolve data, global query params and the global fragment. + information such as route parameters, static data, resolve data, global query params, and the global fragment. @@ -373,21 +445,24 @@ Here are the key `Router` terms and their meanings: + + +{@a sample-app-intro} ## The sample application This guide describes development of a multi-page routed sample application. Along the way, it highlights design decisions and describes key features of the router such as: -* organizing the application features into modules -* 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 `CanActivate` guard (checking route access) -* the `CanActivateChild` guard (checking child route access) -* the `CanDeactivate` guard (ask permission to discard unsaved changes) -* the `Resolve` guard (pre-fetching route data) -* lazy loading feature modules -* the `CanLoad` guard (check before loading feature module assets) +* Organizing the application features into modules. +* 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 `CanActivate` guard (checking route access). +* The `CanActivateChild` guard (checking child route access). +* The `CanDeactivate` guard (ask permission to discard unsaved changes). +* The `Resolve` guard (pre-fetching route data). +* Lazy loading feature modules. +* The `CanLoad` guard (check before loading feature module assets). The guide proceeds as a sequence of milestones as if you were building the app step-by-step. But, it is not a tutorial and it glosses over details of Angular application construction @@ -483,20 +558,20 @@ The router uses the browser's for navigation. Thanks to `pushState`, you can make in-app URL paths look the way you want them to look, e.g. `localhost:3000/crisis-center`. The in-app URLs can be indistinguishable from server URLs. -Modern HTML 5 browsers were the first to support `pushState` which is why many people refer to these URLs as -"HTML 5 style" URLs. +Modern HTML5 browsers were the first to support `pushState` which is why many people refer to these URLs as +"HTML5 style" URLs. -HTML 5 style navigation is the router default. -In the [Browser URL Styles](#browser-url-styles) Appendix, -learn why HTML 5 style is preferred, how to adjust its behavior, and how to switch to the +HTML5 style navigation is the router default. +In the [LocationStrategy and browser URL styles](#browser-url-styles) Appendix, +learn why HTML5 style is preferred, how to adjust its behavior, and how to switch to the older hash (#) style, if necessary. You must **add a <base href> element** to the app's `index.html` for `pushState` routing to work. -The browser uses the base `href` value to prefix *relative* URLs when referencing +The browser uses the `` value to prefix *relative* URLs when referencing CSS files, scripts, and images. -Add the base element just after the `` tag. +Add the `` element just after the `` tag. If the `app` folder is the application root, as it is for this application, set the `href` value in **`index.html`** *exactly* as shown here. @@ -516,7 +591,7 @@ That's why the example code replaces the `` with a script that wri -You should only need this trick for the live example, not production code. +You only need this trick for the live example, not production code. ~~~ @@ -524,7 +599,7 @@ You should only need this trick for the live example, not production code. {@a import} -### Configure the routes for the Router +### Importing from the router library Begin by importing some symbols from the router library. The Router is in its own `@angular/router` package. @@ -540,10 +615,10 @@ You teach the router how to navigate by configuring it with routes. A router must be configured with a list of route definitions. The first configuration defines an array of two routes with simple paths leading to the -`CrisisListComponent` and `HeroListComponent` components. +`CrisisListComponent` and `HeroListComponent`. -Each definition translates to a [Route](../api/router/index/Route-interface.html) object which has a -`path`, the URL path segment for this route, and a +Each definition translates to a [Route](../api/router/index/Route-interface.html) object which has two things: a +`path`, the URL path segment for this route; and a `component`, the component associated with this route. The router draws upon its registry of definitions when the browser URL changes @@ -557,13 +632,13 @@ the router activates an instance of the `CrisisListComponent` and displays its v - When the application requests navigation to the path `/crisis-center`, the router activates an instance of `CrisisListComponent`, displays its view, and updates the browser's address location and history with the URL for that path. -Here is the first configuration. Pass the array of routes to the `RouterModule.forRoot` method. +Here is the first configuration. Pass the array of routes, `appRoutes`, to the `RouterModule.forRoot` method. It returns a module, containing the configured `Router` service provider, plus other providers that the routing library requires. Once the application is bootstrapped, the `Router` performs the initial navigation based on the current browser URL. Adding the configured `RouterModule` to the `AppModule` is sufficient for simple route configurations. As the application grows, you'll want to refactor the routing configuration into a separate file -and create a **[Routing Module](#routing-module)**, a special type of `Service Module` dedicated for the purpose +and create a **[Routing Module](#routing-module)**, a special type of `Service Module` dedicated to the purpose of routing in feature modules. Providing the `RouterModule` in the `AppModule` makes the Router available everywhere in the application. @@ -615,7 +690,7 @@ Learn about the how you can also use the _link parameters array_ in the [appendi {@a router-link-active} ### *RouterLinkActive* binding -On each anchor tag, you also see [Property Bindings](template-syntax.html#property-binding) to +On each anchor tag, you also see [property bindings](template-syntax.html#property-binding) to the `RouterLinkActive` directive that look like `routerLinkActive="..."`. The template expression to the right of the equals (=) contains a space-delimited string of CSS classes @@ -636,6 +711,9 @@ By using `{ exact: true }`, a given `RouterLink` will only be active if its URL They are readily available for you to use in the template. The current state of `app.component.ts` looks like this: + + +{@a wildcard} ### Wildcard route You've created two routes in the app so far, one to `/crisis-center` and the other to `/heroes`. @@ -653,7 +731,7 @@ To test this feature, add a button with a `RouterLink` to the `HeroListComponent Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead and have it navigate to a simple `PageNotFoundComponent`.Create the `PageNotFoundComponent` to display when users visit invalid URLs.As with the other components, add the `PageNotFoundComponent` to the `AppModule` declarations. -Now when the user visits `/sidekicks`, or any other invalid URL, the browser displays the "Page not found". +Now when the user visits `/sidekicks`, or any other invalid URL, the browser displays "Page not found". The browser address bar continues to point to the invalid URL. @@ -710,21 +788,20 @@ Remember to restore the redirect to `pathMatch = 'full'`. Learn more in Victor Savkin's [post on redirects](http://victorsavkin.com/post/146722301646/angular-router-empty-paths-componentless-routes). +### Basics wrap up -A future update to this guide will cover redirects in more detail.### "Getting Started" wrap-up - -You've got a very basic, navigating app, one that can switch between two views +You've got a very basic navigating app, one that can switch between two views when the user clicks a link. -You've learned how to +You've learned how to do the following: -* load the router library -* add a nav bar to the shell template with anchor tags, `routerLink` and `routerLinkActive` directives -* add a `router-outlet` to the shell template where views will be displayed -* configure the router module with `RouterModule.forRoot` -* set the router to compose "HTML 5" browser URLs -* handle invalid routes with a `wildcard` route -* navigate to the default route when the app launches with an empty path +* Load the router library. +* Add a nav bar to the shell template with anchor tags, `routerLink` and `routerLinkActive` directives. +* Add a `router-outlet` to the shell template where views will be displayed. +* Configure the router module with `RouterModule.forRoot`. +* Set the router to compose HTML5 browser URLs. +* handle invalid routes with a `wildcard` route. +* navigate to the default route when the app launches with an empty path. 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 to this milestone. @@ -805,7 +882,7 @@ The starter app's structure looks like this: -Here are the files discussed in this milestone +Here are the files discussed in this milestone. @@ -855,12 +932,15 @@ As the application grows and you make use of more `Router` features, such as gua resolvers, and child routing, you'll naturally want to refactor the routing configuration into its own file. We recommend moving the routing information into a special-purpose module called a *Routing Module*. -The **Routing Module** -* separates routing concerns from other application concerns -* provides a module to replace or remove when testing the application -* provides a well-known location for routing service providers including guards and resolvers -* does **not** [declare components](../cookbook/ngmodule-faq.html#routing-module) -### Refactor routing configuration into a _routing module_ +The **Routing Module** has several characteristics: +* Separates routing concerns from other application concerns. +* Provides a module to replace or remove when testing the application. +* Provides a well-known location for routing service providers including guards and resolvers. +* Does **not** [declare components](../cookbook/ngmodule-faq.html#routing-module). + + +{@a routing-refactor} +### Refactor the routing configuration into a _routing module_ Create a file named `app-routing.module.ts` in the `/app` folder to contain the routing module. @@ -872,7 +952,7 @@ Following convention, add a class name `AppRoutingModule` and export it so you can import it later in `AppModule`. Finally, re-export the Angular `RouterModule` by adding it to the module `exports` array. -By re-exporting the `RouterModule` here and importing `AppRouterModule` in `AppModule`, +By re-exporting the `RouterModule` here and importing `AppRoutingModule` in `AppModule`, the components declared in `AppModule` will have access to router directives such as `RouterLink` and `RouterOutlet`. After these steps, the file should look like this. @@ -880,7 +960,7 @@ After these steps, the file should look like this. {@example 'router/ts/src/app/app-routing.module.1.ts'} Next, update the `app.module.ts` file, -first importing the new-created `AppRoutingModule`from `app-routing.module.ts`, +first importing the newly created `AppRoutingModule`from `app-routing.module.ts`, then replacing `RouterModule.forRoot` in the `imports` array with the `AppRoutingModule`. {@example 'router/ts/src/app/app.module.2.ts'} @@ -902,10 +982,10 @@ The Routing Module is a design choice whose value is most obvious when the confi and includes specialized guard and resolver services. It can seem like overkill when the actual configuration is dead simple. -Some developers skip the Routing Module (e.g., `AppRoutingModule`) when the configuration is simple and -merge the routing configuration directly into the companion module (e.g., `AppModule`). +Some developers skip the Routing Module (for example, `AppRoutingModule`) when the configuration is simple and +merge the routing configuration directly into the companion module (for example, `AppModule`). -We recommend that you choose one pattern or the other and follow that pattern consistently. +Choose one pattern or the other and follow that pattern consistently. Most developers should always implement a Routing Module for the sake of consistency. It keeps the code clean when configuration becomes complex. @@ -915,12 +995,12 @@ It is where developers expect to find and expand routing configuration. ## Milestone 3: Heroes feature -You've seen how to navigate using the `RouterLink` directive, -now you'll learn how to +You've seen how to navigate using the `RouterLink` directive. +Now you'll learn the following: -* Organize the app and routes into *feature areas* using modules -* Navigate imperatively from one component to another -* Pass required and optional information in route parameters +* Organize the app and routes into *feature areas* using modules. +* Navigate imperatively from one component to another. +* Pass required and optional information in route parameters. This example recreates the heroes feature in the "Services" episode of the [Tour of Heroes tutorial](../tutorial/toh-pt4.html "Tour of Heroes: Services"), @@ -942,7 +1022,10 @@ Most developers prefer to put each feature area in its own folder. You are about to break up the app into different *feature modules*, each with its own concerns. Then you'll import into the main module and navigate among them. -### Add Heroes functionality + + +{@a heroes-functionality} +### Add heroes functionality Follow these steps: @@ -952,7 +1035,7 @@ Follow these steps: - Copy into it the contents of the `app.component.ts` from the "Services" tutorial. - Make a few minor but necessary changes: - - Delete the `selector` (routed component don't need them). + - Delete the `selector` (routed components don't need them). - Delete the `

    `. - Relabel the `

    ` to `

    HEROES

    `. - Delete the `` at the bottom of the template. @@ -994,6 +1077,9 @@ When you're done, you'll have these *hero management* files: + + +{@a hero-routing-requirements} ### *Hero* feature routing requirements The heroes feature has two interacting components, the hero list and the hero detail. @@ -1017,25 +1103,27 @@ using the same techniques you learned while creating the `AppRoutingModule`. {@example 'router/ts/src/app/heroes/heroes-routing.module.ts'} -Put the Routing Module file in the same folder as its companion module file. +Put the routing module file in the same folder as its companion module file. Here both `heroes-routing.module.ts` and `heroes.module.ts` are in the same `src/app/heroes` folder. -We recommend giving each feature module its own route configuration file. +Consider giving each feature module its own route configuration file. It may seem like overkill early when the feature routes are simple. But routes have a tendency to grow more complex and consistency in patterns pays off over time. -Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes. +Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes, and export the `HeroRoutingModule` class. Now that you have routes for the `Heroes` module, register them with the `Router` via the `RouterModule` _almost_ as you did in the `AppRoutingModule`. There is a small but critical difference. -In the `AppRoutingModule`, you used the static `RouterModule.`**`forRoot`** method to register the routes and application level service providers. -In a feature module you use static **`forChild`** method. +In the `AppRoutingModule`, you used the static **`RouterModule.forRoot`** method to register the routes and application level service providers. +In a feature module you use the static **`forChild`** method. Only call `RouterModule.forRoot` in the root `AppRoutingModule` (or the `AppModule` if that's where you register top level application routes). -In any other module, you must call the `RouterModule.`**`forChild`** method to register additional routes. +In any other module, you must call the **`RouterModule.forChild`** method to register additional routes. + +{@a adding-routing-module} ### Add the routing module to the _HeroesModule_ Add the `HeroRoutingModule` to the `HeroModule` just as you added `AppRoutingModule` to the `AppModule`. @@ -1048,6 +1136,9 @@ The finished `HeroesModule` looks like this: {@example 'router/ts/src/app/heroes/heroes.module.ts'} + + +{@a remove-duplicate-hero-routes} ### Remove duplicate hero routes The hero routes are currently defined in _two_ places: in the `HeroesRoutingModule`, @@ -1068,7 +1159,7 @@ These are concerns at the top level of the application itself. The heroes feature module is ready, but the application doesn't know about the `HeroesModule` yet. Open `app.module.ts` and revise it as follows. -Import the `HeroesModule` and add it to the `imports` array in the `@NgModule` metadata of the `AppModule` +Import the `HeroesModule` and add it to the `imports` array in the `@NgModule` metadata of the `AppModule`. Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it's now provided by the `HeroesModule`. This is important. There can be only _one_ owner for a declared component. @@ -1098,7 +1189,7 @@ The order of route configuration matters. The router accepts the first route that matches a navigation request path. When all routes were in one `AppRoutingModule`, -you put the default and [wildcard](#wildcard-route) routes last, after the `/heroes` route, +you put the default and [wildcard](#wildcard) routes last, after the `/heroes` route, so that the router had a chance to match a URL to the `/heroes` route _before_ hitting the wildcard route and navigating to "Page not found". @@ -1115,6 +1206,9 @@ Reverse the routing modules and see for yourself that a click of the heroes link results in "Page not found". Learn about inspecting the runtime router configuration [below](#inspect-config "Inspect the router config"). + + +{@a route-def-with-parameter} ### Route definition with a parameter Return to the `HeroesRoutingModule` and look at the route definitions again. @@ -1175,17 +1269,14 @@ You can use this same syntax in a `RouterLink` if you decide later to navigate i ### Setting the route parameters in the list view After navigating to the `HeroDetailComponent`, you expect to see the details of the selected hero. -You'll need *two* pieces of information: the routing path to the component and the hero's `id`. +You need *two* pieces of information: the routing path to the component and the hero's `id`. Accordingly, the _link parameters array_ has *two* items: the routing _path_ and a _route parameter_ that specifies the `id` of the selected hero. -The router composes the destination URL from this array: +The router composes the destination URL from the array like this: `localhost:3000/hero/15`. -{@a get-route-parameter} -### Getting the route parameter in the details view - How does the target `HeroDetailComponent` learn about that `id`? Don't analyze the URL. Let the router do it. @@ -1234,7 +1325,7 @@ Angular calls the `ngOnInit` method shortly after creating an instance of the `H so the hero will be retrieved in time to use it. Learn more about the `ngOnInit` method and other component lifecycle hooks in the [Lifecycle Hooks](lifecycle-hooks.html) guide. -Since the parameters are provided as an `Observable`, you use the _switchMap_ operator to +Since the parameters are provided as an `Observable`, you use the `switchMap` operator to provide them for the `id` parameter by name and tell the `HeroService` to fetch the hero with that `id`. The `switchMap` operator allows you to perform an action with the current value of the `Observable`, @@ -1246,9 +1337,9 @@ while still retrieving a hero. Use the `subscribe` method to detect `id` changes and to (re)set the retrieved `Hero`. -

    - Observable params and component re-use -

    + +{@a reuse} +#### Observable params and component reuse In this example, you retrieve the route params from an `Observable`. That implies that the route params can change during the lifetime of this component. @@ -1332,8 +1423,9 @@ You'll implement this feature in a moment by including the viewed hero's `id` in the URL as an optional parameter when returning from the `HeroDetailComponent`. Optional information takes other forms. Search criteria are often loosely structured, e.g., `name='wind*'`. -Multiple values are common — `after='12/31/2015' & before='1/1/2017'` — in no particular order — - `before='1/1/2017' & after='12/31/2015'` — in a variety of formats — `during='currentYear'` . +Multiple values are common—`after='12/31/2015' & before='1/1/2017'`—in no +particular order—`before='1/1/2017' & after='12/31/2015'`— in a +variety of formats—`during='currentYear'`. These kinds of parameters don't fit easily in a URL *path*. Even if you could define a suitable URL token scheme, doing so greatly complicates the pattern matching required to translate an incoming URL to a named route. @@ -1346,7 +1438,10 @@ Define _optional_ parameters in a separate object _after_ you define the require In general, prefer a *required route parameter* when the value is mandatory (for example, if necessary to distinguish one route path from another); -prefer an *optional parameter* when the value is optional, complex, and/or multi-variate. +prefer an *optional parameter* when the value is optional, complex, and/or multivariate. + + +{@a optionally-selecting} ### Heroes list: optionally selecting a hero When navigating to the `HeroDetailComponent` you specified the _required_ `id` of the hero-to-edit in the @@ -1381,7 +1476,7 @@ The optional route parameters are not separated by "?" and "&" as they would be They are **separated by semicolons ";"** This is *matrix URL* notation — something you may not have seen before. -*Matrix URL* notation is an idea first floated +*Matrix URL* notation is an idea first introduced in a [1996 proposal](http://www.w3.org/DesignIssues/MatrixURIs.html) by the founder of the web, Tim Berners-Lee. Although matrix notation never made it into the HTML standard, it is legal and @@ -1392,13 +1487,15 @@ support for the matrix notation across browsers. The syntax may seem strange to you but users are unlikely to notice or care as long as the URL can be emailed and pasted into a browser address bar as this one can. + +{@a route-parameters-activated-route} ### Route parameters in the *ActivatedRoute* service The list of heroes is unchanged. No hero row is highlighted. The *does* highlight the selected row because it demonstrates the final state of the application which includes the steps you're *about* to cover. -At the moment you're describing the state of affairs *prior* to those steps.The `HeroListComponent` isn't expecting any parameters at all and wouldn't know what to do with them. +At the moment this guide is describing the state of affairs *prior* to those steps.The `HeroListComponent` isn't expecting any parameters at all and wouldn't know what to do with them. You can change that. Previously, when navigating from the `HeroListComponent` to the `HeroDetailComponent`, @@ -1408,16 +1505,16 @@ You injected that service in the constructor of the `HeroDetailComponent`. This time you'll be navigating in the opposite direction, from the `HeroDetailComponent` to the `HeroListComponent`. -First you extend the router import statement to include the `ActivatedRoute` service symbol; +First you extend the router import statement to include the `ActivatedRoute` service symbol: Import the `switchMap` operator to perform an operation on the `Observable` of route parameters. Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor. -The ActivatedRoute.params property is an Observable of route parameters. The params emits new id values -when the user navigates to the component. In ngOnInit you subscribe to those values, set the selectedId, +The `ActivatedRoute.params` property is an `Observable` of route parameters. The `params` emits new `id` values +when the user navigates to the component. In `ngOnInit` you subscribe to those values, set the `selectedId`, and get the heroes. All route/query parameters are strings. -The (+) in front of the `params['id']` expression is a JavaScript trick to convert the string to an integer.Add an `isSelected` method that returns true when a hero's id matches the selected id. -Finally, you update the template with a [Class Binding](template-syntax.html#class-binding) to that `isSelected` method. +The (+) in front of the `params['id']` expression is a JavaScript trick to convert the string to an integer.Add an `isSelected` method that returns `true` when a hero's `id` matches the selected `id`. +Finally, update the template with a [class binding](template-syntax.html#class-binding) to that `isSelected` method. The binding adds the `selected` CSS class when the method returns `true` and removes it when `false`. Look for it within the repeated `
  • ` tag as shown here: When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected: @@ -1427,22 +1524,22 @@ When the user navigates from the heroes list to the "Magneta" hero and back, "Ma The optional `foo` route parameter is harmless and continues to be ignored. -

    - Adding animations to the routed component -

    +{@a route-animation} +### Adding animations to the routed component The heroes feature module is almost complete, but what is a feature without some smooth transitions? -In this section you'll add some [animations](../guide/animations.html) to the *Hero Detail* component. +This section shows you how to add some [animations](../guide/animations.html) +to the `HeroDetailComponent`. Create an `animations.ts` file in the root `src/app/` folder. The contents look like this:This file does the following: * Imports the animation symbols that build the animation triggers, control state, and manage transitions between states. -* Exports a constant named `slideInDownAnimation` set to an animation trigger named *routeAnimation*; +* Exports a constant named `slideInDownAnimation` set to an animation trigger named *`routeAnimation`*; animated components will refer to this name. -* Specifies the _wildcard state_ that matches any animation state that the route component is in. +* Specifies the _wildcard state_ , `*`, that matches any animation state that the route component is in. * Defines two *transitions*, one to ease the component in from the left of the screen as it enters the application view (`:enter`), the other to animate the component down as it leaves the application view (`:leave`). @@ -1453,24 +1550,28 @@ Add the `HostBinding` decorator to the imports from `@angular/core`; you'll nee Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`. -Then add three `@HostBinding` properties to the class to set the animation and styles for the route component's element.The `'@routeAnimation'` passed to the first `@HostBinding` matches the name of the `slideInDownAnimation` _trigger_. +Then add three `@HostBinding` properties to the class to set the animation and styles for the route component's element.The `'@routeAnimation'` passed to the first `@HostBinding` matches +the name of the `slideInDownAnimation` _trigger_, `routeAnimation`. Set the `routeAnimation` property to `true` because you only care about the `:enter` and `:leave` states. The other two `@HostBinding` properties style the display and position of the component. The `HeroDetailComponent` will ease in from the left when routed to and will slide down when navigating away. -Applying route animations to individual components is something you'd rather not do throughout the entire application. -It would be better to animate routes based on _route paths_, a topic to cover in a future update to this guide. -### Milestone 3 wrap-up +Applying route animations to individual components works for a simple demo, but in a real life app, +it is better to animate routes based on _route paths_. -You've learned how to -* organize the app into *feature areas* -* navigate imperatively from one component to another -* pass information along in route parameters and subscribe to them in the component -* import the feature area NgModule into the `AppModule` -* apply animations to the route component +{@a milestone-3-wrap-up} +### Milestone 3 wrap up + +You've learned how to do the following: + +* Organize the app into *feature areas*. +* Navigate imperatively from one component to another. +* Pass information along in route parameters and subscribe to them in the component. +* Import the feature area NgModule into the `AppModule`. +* Apply animations to the route component. After these changes, the folder structure looks like this: @@ -1630,11 +1731,11 @@ Begin by imitating the heroes feature: - Delete the placeholder crisis center file. - Create !{_an} `!{_appDir}/crisis-center` folder. -- Copy the files from `!{_appDir}/heroes` into the new crisis center folder, but -- Change every mention of "hero" to "crisis", and "heroes" to "crises". +- Copy the files from `!{_appDir}/heroes` into the new crisis center folder. +- In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises". You'll turn the `CrisisService` into a purveyor of mock crises instead of mock heroes: -The resulting crisis center is a foundation for introducing a new concept — **child routing**. +The resulting crisis center is a foundation for introducing a new concept—**child routing**. You can leave *Heroes* in its current state as a contrast with the *Crisis Center* and decide later if the differences are worthwhile. @@ -1646,14 +1747,17 @@ In keeping with the changes to the *Crisis Center* won't affect the `!{_AppModuleVsAppComp}` or any other feature's component. - ~~~ -### A Crisis center with child routes -You'll organize the crisis center to conform to the following recommended pattern for Angular applications: -* Each feature area resides in its own folder +{@a crisis-child-routes} +### A crisis center with child routes + +This section shows you how to organize the crisis center +to conform to the following recommended pattern for Angular applications: + +* Each feature area resides in its own folder. * Each feature has its own Angular feature module. * Each area has its own area root component. * Each area root component has its own router outlet and child routes. @@ -1665,15 +1769,18 @@ If your app had many feature areas, the app component trees might look like this Component Tree + + +{@a child-routing-component} ### Child routing component Add the following `crisis-center.component.ts` to the `crisis-center` folder: -Much like the `AppComponent`, the `CrisisCenterComponent` is the +The `CrisisCenterComponent` has the following in common with the `AppComponent`: -- *Root* of the crisis center area, -just as `AppComponent` is the root of the entire application -- *Shell* for the crisis management feature area, -just as the `AppComponent` is a shell to manage the high-level workflow +- It is the *root* of the crisis center area, +just as `AppComponent` is the root of the entire application. +- It is a *shell* for the crisis management feature area, +just as the `AppComponent` is a shell to manage the high-level workflow. Like most shells, the `CrisisCenterComponent` class is very simple, simpler even than `AppComponent`: it has no business logic, and its template has no links, just a title and @@ -1682,6 +1789,9 @@ it has no business logic, and its template has no links, just a title and Unlike `AppComponent`, and most other components, it _lacks a selector_. It doesn't _need_ one since you don't *embed* this component in a parent template, instead you use the router to *navigate* to it. + + +{@a child-route-config} ### Child route configuration The `CrisisCenterComponent` is a *routing component* like the `AppComponent`. @@ -1757,7 +1867,7 @@ If you changed the parent `/crisis-center` path, you would have to change the li You can free the links from this dependency by defining paths that are **relative** to the current URL segment. Navigation _within_ the feature area remains intact even if you change the parent route path to the feature. -Here's an example +Here's an example: The router supports directory-like syntax in a _link parameters list_ to help guide route name lookup: @@ -1775,6 +1885,9 @@ After the _link parameters array_, add an object with a `relativeTo` property se The router then calculates the target URL based on the active route's location. **Always** specify the complete _absolute_ path when calling router's `navigateByUrl` method. + + +{@a nav-to-crisis} ### Navigate to crisis detail with a relative URL Update the *Crisis List* `onSelect` method to use relative navigation so you don't have @@ -1786,7 +1899,7 @@ If you were using a `RouterLink` to navigate instead of the `Router` service, yo link parameters array, but you wouldn't provide the object with the `relativeTo` property. The `ActivatedRoute` is implicit in a `RouterLink` directive. Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation. -Notice that the path goes up a level (`../`) syntax. +Notice that the path goes up a level using the `../` syntax. If the current crisis `id` is `3`, the resulting path back to the crisis list is `/crisis-center/;id=3;foo=foo`. @@ -1803,7 +1916,7 @@ Clearly you can't put the popup in the same outlet as the other pages. Until now, you've defined a single outlet and you've nested child routes under that outlet to group routes together. -The router only supports one primary _unnamed_ outlet per template, +The router only supports one primary _unnamed_ outlet per template. A template can also have any number of _named_ outlets. Each named outlet has its own set of routes with their own components. @@ -1821,7 +1934,7 @@ Named outlets are the targets of _secondary routes_. Secondary routes look like primary routes and you configure them the same way. They differ in a few key respects. -* They are independent of each other +* They are independent of each other. * They work in combination with other routes. * They are displayed in named outlets. @@ -1850,21 +1963,23 @@ Here's the component and its template: It looks about the same as any other component you've seen in this guide. -There are two noteworthy differences +There are two noteworthy differences. -Note that the `send` method simulates latency by waiting a second before "sending" the message and closing the popup. +Note that the `send()` method simulates latency by waiting a second before "sending" the message and closing the popup. -The `closePopup` method closes the popup view by navigating to the "popup" outlet with a `null`. +The `closePopup()` method closes the popup view by navigating to the popup outlet with a `null`. That's a peculiarity covered [below](#clear-secondary-routes). As with other application components, you add the `ComposeMessageComponent` to the `declarations` of an `NgModule`. Do so in the `AppModule`. + +{@a add-secondary-route} #### Add a secondary route Open the `AppRoutingModule` and add a new `compose` route to the `appRoutes`.The `path` and `component` properties should be familiar. -There's a new property `outlet` set to `'popup'`. -This route now targets the "popup" outlet and the `ComposeMessageComponent` will display there. +There's a new property, `outlet`, set to `'popup'`. +This route now targets the popup outlet and the `ComposeMessageComponent` will display there. The user needs a way to open the popup. Open the `AppComponent` and add a "Contact" link.Although the `compose` route is pinned to the "popup" outlet, that's not sufficient for wiring the route to a `RouterLink` directive. @@ -1888,10 +2003,9 @@ you can target multiple outlets with multiple routes in the same `RouterLink` di You're not actually doing that here. But to target a named outlet, you must use the richer, more verbose syntax. -

    - Secondary Route Navigation: merging routes during navigation -

    +{@a secondary-route-navigation} +#### Secondary route navigation: merging routes during navigation Navigate to the _Crisis Center_ and click "Contact". you should see something like the following URL in the browser address bar. @@ -1903,7 +2017,7 @@ you should see something like the following URL in the browser address bar. The interesting part of the URL follows the `...`: * The `crisis-center` is the primary navigation. * Parentheses surround the secondary route. -* The secondary route consist of an outlet name (`popup`), then a `colon` separator, followed with the secondary route path (`compose`) +* The secondary route consists of an outlet name (`popup`), a `colon` separator, and the secondary route path (`compose`). Click the _Heroes_ link and look at the URL again. @@ -1931,11 +2045,21 @@ As you've learned, a component in an outlet persists until you navigate away to Secondary outlets are no different in this regard. Each secondary outlet has its own navigation, independent of the navigation driving the primary outlet. -Changing a current route that displays in the primary outlet has no effect on the "popup" outlet. -That's why the "popup" stays visible as you navigate among the crises and heroes. +Changing a current route that displays in the primary outlet has no effect on the popup outlet. +That's why the popup stays visible as you navigate among the crises and heroes. Clicking the "send" or "cancel" buttons _does_ clear the popup view. -To see how, look at the `ComposeMessageComponent.closePopup` method again: +To see how, look at the `closePopup()` method again:It navigates imperatively with the `Router.navigate()` method, passing in a [link parameters array](#link-parameters-array). + +Like the array bound to the _Contact_ `RouterLink` in the `AppComponent`, +this one includes an object with an `outlets` property. +The `outlets` property value is another object with outlet names for keys. +The only named outlet is `'popup'`. + +This time, the value of `'popup'` is `null`. That's not a route, but it is a legitimate value. +Setting the popup `RouterOutlet` to `null` clears the outlet and removes +the secondary popup route from the current URL. + ## Milestone 5: Route guards At the moment, *any* user can navigate *anywhere* in the application *anytime*. @@ -1951,8 +2075,8 @@ You can add _guards_ to the route configuration to handle these scenarios. A guard's return value controls the router's behavior: -* if it returns `true`, the navigation process continues -* if it returns `false`, the navigation process stops and the user stays put +* If it returns `true`, the navigation process continues. +* If it returns `false`, the navigation process stops and the user stays put. The guard can also tell the router to navigate elsewhere, effectively canceling the current navigation. The guard *might* return its boolean answer synchronously. @@ -1965,27 +2089,27 @@ router will wait for the observable to resolve to `true` or `false`. The router supports multiple 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. [CanActivateChild](../api/router/index/CanActivateChild-interface.html) to mediate navigation *to* a child route. +2. [`CanActivateChild()`](../api/router/index/CanActivateChild-interface.html) to mediate navigation *to* a child route. -3. [CanDeactivate](../api/router/index/CanDeactivate-interface.html) to mediate navigation *away* from the current route. +3. [`CanDeactivate`](../api/router/index/CanDeactivate-interface.html) to mediate navigation *away* from the current route. -4. [Resolve](../api/router/index/Resolve-interface.html) to perform route data retrieval *before* route activation. +4. [`Resolve`](../api/router/index/Resolve-interface.html) to perform route data retrieval *before* route activation. -5. [CanLoad](../api/router/index/CanLoad-interface.html) to mediate navigation *to* a feature module loaded _asynchronously_. +5. [`CanLoad`](../api/router/index/CanLoad-interface.html) to mediate navigation *to* a feature module loaded _asynchronously_. You can have multiple guards at every level of a routing hierarchy. -The router checks the `CanDeactivate` and `CanActivateChild` 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 the feature module -is loaded asynchronously, the `CanLoad` guard is checked before the module is loaded. +The router checks the `CanDeactivate()` and `CanActivateChild()` guards first, from the deepest child route to the top. +Then it checks the `CanActivate()` guards from the top down to the deepest child route. If the feature module +is loaded asynchronously, the `CanLoad()` guard is checked before the module is loaded. If _any_ guard returns false, pending guards that have not completed will be canceled, and the entire navigation is canceled. -You'll see several examples over the next few sections. +There are several examples over the next few sections. {@a can-activate-guard} -### *CanActivate*: requiring authentication +### _CanActivate_: requiring authentication Applications often restrict access to a feature area based on who the user is. You could permit access only to authenticated users or to users with a specific role. @@ -2078,16 +2202,15 @@ feature module, a dashboard route and two unfinished components to manage crises Since the admin dashboard `RouterLink` is an empty path route in the `AdminComponent`, it is considered a match to any route within the admin feature area. You only want the `Dashboard` link to be active when the user visits that route. -Add an additional binding to the `Dashboard` routerLink, -`[routerLinkActiveOptions]="{ exact: true }"` which marks the `./` link as active when +Adding an additional binding to the `Dashboard` routerLink, +`[routerLinkActiveOptions]="{ exact: true }"`, marks the `./` link as active when the user navigates to the `/admin` URL and not when navigating to any of the child routes. The initial admin routing configuration: -

    - Component-Less Route: grouping routes without a component -

    -Looking at the child route under the `AdminComponent`,there is a `path` and a `children` +{@a component-less-route} +### Component-less route: grouping routes without a component +Looking at the child route under the `AdminComponent`, there is a `path` and a `children` property but it's not using a `component`. You haven't made a mistake in the configuration. You've defined a _component-less_ route. @@ -2095,9 +2218,12 @@ You've defined a _component-less_ route. The goal is to group the `Crisis Center` management routes under the `admin` path. You don't need a component to do it. A _component-less_ route makes it easier to [guard child routes](#can-activate-child-guard). -Next, import the `AdminModule` into the `app.module.ts` and add it to the `imports` array +Next, import the `AdminModule` into `app.module.ts` and add it to the `imports` array to register the admin routes. Add an "Admin" link to the `AppComponent` shell so that users can get to this feature. + + +{@a guard-admin-feature} #### Guard the admin feature Currently every route within the *Crisis Center* is open to everyone. @@ -2105,17 +2231,21 @@ The new *admin* feature should be accessible only to authenticated users. You could hide the link until the user logs in. But that's tricky and difficult to maintain. -Instead you'll write a `CanActivate` guard to redirect anonymous users to the login page when they try to enter the admin area. +Instead you'll write a `CanActivate()` guard to redirect anonymous users to the +login page when they try to enter the admin area. -This is a general purpose guard — you can imagine other features that require authenticated users — -so you create an `auth-guard.service.ts` in the application root folder. +This is a general purpose guard—you can imagine other features +that require authenticated users—so you create an +`auth-guard.service.ts` in the application root folder. At the moment you're interested in seeing how guards work so the first version does nothing useful. It simply logs to console and `returns` true immediately, allowing navigation to proceed: -Next you open `admin-routing.module.ts `, import the `AuthGuard` class, and -update the admin route with a `CanActivate` guard property that references it: +Next, open `admin-routing.module.ts `, import the `AuthGuard` class, and +update the admin route with a `CanActivate()` guard property that references it: The admin feature is now protected by the guard, albeit protected poorly. + +{@a teach-auth} #### Teach *AuthGuard* to authenticate Make the `AuthGuard` at least pretend to authenticate. @@ -2124,7 +2254,8 @@ The `AuthGuard` should call an application service that can login a user and ret Here's a demo `AuthService`: Although it doesn't actually log in, it has what you need for this discussion. It has an `isLoggedIn` flag to tell you 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. +Its `login` method simulates an API call to an external service by returning an +Observable that resolves successfully after a short pause. The `redirectUrl` property will store the attempted URL so you can navigate to it after authenticating. Revise the `AuthGuard` to call it. @@ -2138,9 +2269,12 @@ The `ActivatedRouteSnapshot` contains the _future_ route that will be activated contains the _future_ `RouterState` of the application, should you pass through the guard check. If the user is not logged in, you store the attempted URL the user came from using the `RouterStateSnapshot.url` and -tell the router to navigate to a login page — a page you haven't created yet. -This secondary navigation automatically cancels the current navigation; you return `false` just to be clear about that. +tell the router to navigate to a login page—a page you haven't created yet. +This secondary navigation automatically cancels the current navigation; `checkLogin()` returns +`false` just to be clear about that. + +{@a add-login-component} #### Add the *LoginComponent* You need a `LoginComponent` for the user to log in to the app. After logging in, you'll redirect @@ -2148,7 +2282,7 @@ to the stored URL if available, or use the default URL. There is nothing new about this component or the way you wire it into the router configuration. Register a `/login` route in the `login-routing.module.ts` and add the necessary providers to the `providers` -array. In the `app.module.ts`, import the `LoginComponent` and add it to the `AppModule` `declarations`. +array. In `app.module.ts`, import the `LoginComponent` and add it to the `AppModule` `declarations`. Import and add the `LoginRoutingModule` to the `AppModule` imports as well. @@ -2175,9 +2309,9 @@ Guards and the service providers they require _must_ be provided at the module-l the Router access to retrieve these services from the `Injector` during the navigation process. The same rule applies for feature modules loaded [asynchronously](#asynchronous-routing). -

    - CanActivateChild: guarding child routes -

    + +{@a can-activate-child-guard} +### _CanActivateChild_: guarding child routes You can also protect child routes with the `CanActivateChild` guard. The `CanActivateChild` guard is similar to the `CanActivate` guard. @@ -2187,19 +2321,19 @@ You protected the admin feature module from unauthorized access. You should also protect child routes _within_ the feature module. Extend the `AuthGuard` to protect when navigating between the `admin` routes. -Open the `auth-guard.service.ts` and add the `CanActivateChild` interface to the imported tokens from the router package. +Open `auth-guard.service.ts` and add the `CanActivateChild` interface to the imported tokens from the router package. -Next, implement the `canActivateChild` method which takes the same arguments as the `canActivate` method: +Next, implement the `CanActivateChild` method which takes the same arguments as the `CanActivate` method: an `ActivatedRouteSnapshot` and `RouterStateSnapshot`. -The `canActivateChild` can return an `Observable` or `Promise` for async checks and a `boolean` for sync checks. -This one returns a `boolean` +The `CanActivateChild` method can return an `Observable` or `Promise` for +async checks and a `boolean` for sync checks. +This one returns a `boolean`: Add the same `AuthGuard` to the `component-less` admin route to protect all other child routes at one time instead of adding the `AuthGuard` to each route individually. -

    - CanDeactivate: handling unsaved changes -

    +{@a can-deactivate-guard} +### _CanDeactivate_: handling unsaved changes Back in the "Heroes" workflow, the app accepts every change to a hero immediately without hesitation or validation. In the real world, you might have to accumulate the users changes. @@ -2211,20 +2345,21 @@ cancels and reverts all changes. What do you do about unapproved, unsaved changes when the user navigates away? You can't just leave and risk losing the user's changes; that would be a terrible experience. -You'd prefer to pause and let the user decide what to do. +It's better to pause and let the user decide what to do. If the user cancels, you'll stay put and allow more changes. If the user approves, the app can save. You still might delay navigation until the save succeeds. If you let the user move to the next screen immediately and -the save failed (perhaps the data are ruled invalid), you would have lost the context of the error. +the save were to fail (perhaps the data are ruled invalid), you would lose the context of the error. -You can't block while waiting for the server — that's not possible in a browser. +You can't block while waiting for the server—that's not possible in a browser. You need to stop the navigation while you wait, asynchronously, for the server to return with its answer. You need the `CanDeactivate` guard. +{@a cancel-save} ### Cancel and save The sample application doesn't talk to a server. @@ -2232,8 +2367,8 @@ Fortunately, you have another way to demonstrate an asynchronous router hook. Users update crisis information in the `CrisisDetailComponent`. Unlike the `HeroDetailComponent`, the user changes do not update the crisis entity immediately. -Update the entity when the user presses the *Save* button. -Discard the changes when the user presses the *Cancel* button. +Instead, the app updates the entity when the user presses the *Save* button and +discards the changes when the user presses the *Cancel* button. Both buttons navigate back to the crisis list after save or cancel. What if the user tries to navigate away without saving or canceling? @@ -2241,13 +2376,13 @@ The user could push the browser back button or click the heroes link. Both actions trigger a navigation. Should the app save or cancel automatically? -You'll do neither. Instead you'll ask the user to make that choice explicitly +This demo does neither. Instead, it asks the user to make that choice explicitly in a confirmation dialog box that *waits asynchronously for the user's answer*. You could wait for the user's answer with synchronous, blocking code. -The 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.The `DialogService` (provided in the `AppModule` for app-wide use) does the asking. +The 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.The `DialogService`, provided in the `AppModule` for app-wide use, does the asking. It returns a [promise](http://exploringjs.com/es6/ch_promises.html) that *resolves* when the user eventually decides what to do: either @@ -2255,59 +2390,65 @@ to discard changes and navigate away (`true`) or to preserve the pending changes {@a CanDeactivate} -Create a _guard_ that checks for the presence of a `canDeactivate` method in a component - any component. +Create a _guard_ that checks for the presence of a `canDeactivate` method in a component—any component. The `CrisisDetailComponent` will have this method. But the guard doesn't have to know that. The guard shouldn't know the details of any component's deactivation method. -It need only detect that the component has a `canDeactivate` method and call it. +It need only detect that the component has a `canDeactivate()` method and call it. This approach makes the guard reusable. {@example 'router/ts/src/app/can-deactivate-guard.service.ts'} -Alternatively, You could make a component-specific `CanDeactivate` guard for the `CrisisDetailComponent`. The `canDeactivate` method provides you -with the current instance of the `component`, the current `ActivatedRoute` and `RouterStateSnapshot` in case you needed to access -some external information. This would be useful if you only wanted to use this guard for this component and needed to ask the component's -properties in or to confirm whether the router should allow navigation away from it. -Looking back at the `CrisisDetailComponent`, you have implemented the confirmation workflow for unsaved changes. +Alternatively, you could make a component-specific `CanDeactivate` guard for the `CrisisDetailComponent`. +The `canDeactivate()` method provides you with the current +instance of the `component`, the current `ActivatedRoute`, +and `RouterStateSnapshot` in case you needed to access +some external information. This would be useful if you only +wanted to use this guard for this component and needed to get +the component's properties or confirm whether the router should allow navigation away from it. +Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes. Notice that the `canDeactivate` method *can* return synchronously; it returns `true` immediately if there is no crisis or there are no pending changes. But it can also return a `Promise` or an `Observable` and the router will wait for that to resolve to truthy (navigate) or falsy (stay put). Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array. -Add the `Guard` to the main `AppRoutingModule` `providers` so the `Router` can inject it during the navigation process. +Add the `Guard` to the main `AppRoutingModule` `providers` !{_array} so the +`Router` can inject it during the navigation process. {@example 'router/ts/src/app/app-routing.module.4.ts'} Now you have given the user a safeguard against unsaved changes. -

    - Resolve: pre-fetching component data -

    -In the `Hero Detail` and `Crisis Detail`, you waited until the route was activated to fetch the respective hero or crisis. +{@a resolve-guard} +### _Resolve_: pre-fetching component data -This worked well, but you can do better. -If you were using a real world api, there might be some delay before the data to display is returned from the server. +In the `Hero Detail` and `Crisis Detail`, the app waited until the route was activated to fetch the respective hero or crisis. + +This worked well, but there's a better way. +If you were using a real world API, there might be some delay before the data to display is returned from the server. You don't want to display a blank component while waiting for the data. -You prefer to pre-fetch data from the server so it's ready the moment the route is activated. -You'd like to handle errors before routing to the component. +It's preferable to pre-fetch data from the server so it's ready the +moment the route is activated. This also allows you to handle errors before routing to the component. There's no point in navigating to a crisis detail for an `id` that doesn't have a record. -You'd rather send the user back to the `Crisis List` where you only show valid crisis centers. +It'd be better to send the user back to the `Crisis List` that shows only valid crisis centers. In summary, you want to delay rendering the routed component until all necessary data have been fetched. You need a *resolver*. + +{@a fetch-before-navigating} ### Fetch data before navigating At the moment, the `CrisisDetailComponent` retrieves the selected crisis. If the crisis is not found, it navigates back to the crisis list view. -The experience might be better all of this were handled first, before the route is activated. -A `CrisisDetailResolver` service could retrieve a `Crisis` or navigate away if the `Crisis` does not existing +The experience might be better if all of this were handled first, before the route is activated. +A `CrisisDetailResolver` service could retrieve a `Crisis` or navigate away if the `Crisis` does not exist _before_ activating the route and creating the `CrisisDetailComponent`. Create the `crisis-detail-resolver.service.ts` file within the `Crisis Center` feature area. @@ -2315,12 +2456,14 @@ Create the `crisis-detail-resolver.service.ts` file within the `Crisis Center` f {@example 'router/ts/src/app/crisis-center/crisis-detail-resolver.service.ts'} -Take the relevant parts of the crisis retrieval logic in `CrisisDetailComponent.ngOnInit` move them into the `CrisisDetailResolver`. -Import the `Crisis` model and `CrisisService` and also the `Router` so you can navigate elsewhere if you can't fetch the crisis. +Take the relevant parts of the crisis retrieval logic in `CrisisDetailComponent.ngOnInit` +and move them into the `CrisisDetailResolver`. +Import the `Crisis` model, `CrisisService`, and the `Router` +so you can navigate elsewhere if you can't fetch the crisis. Be explicit. Implement the `Resolve` interface with a type of `Crisis`. -Inject the `CrisisService` and `Router` and implement the `resolve` method. +Inject the `CrisisService` and `Router` and implement the `resolve()` method. That method could return a `Promise`, an `Observable`, or a synchronous return value. The `CrisisService.getCrisis` method returns a promise. @@ -2328,9 +2471,10 @@ Return that promise to prevent the route from loading until the data is fetched. If it doesn't return a valid `Crisis`, navigate the user back to the `CrisisListComponent`, canceling the previous in-flight navigation to the `CrisisDetailComponent`. -Import this resolver in the `crisis-center-routing.module.ts` and add a `resolve` object to the `CrisisDetailComponent` route configuration. +Import this resolver in the `crisis-center-routing.module.ts` +and add a `resolve` object to the `CrisisDetailComponent` route configuration. -Remember to add the `CrisisDetailResolver` service to the `CrisisCenterRoutingModule`'s `providers`. +Remember to add the `CrisisDetailResolver` service to the `CrisisCenterRoutingModule`'s `providers` !{_array}. The `CrisisDetailComponent` should no longer fetch the crisis. Update the `CrisisDetailComponent` to get the crisis from the `ActivatedRoute.data.crisis` property instead; that's where you said it should be when you re-configured the route. @@ -2425,8 +2569,9 @@ Update the `AuthGuard` to provide a `session_id` query that will remain after na Add an `anchor` element so you can jump to a certain point on the page. Add the `NavigationExtras` object to the `router.navigate` method that navigates you to the `/login` route. -You can also preserve query parameters and fragments across navigations without having to re-provide them -when navigating. In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigate` function +You can also preserve query parameters and fragments across navigations without having to provide them +again when navigating. In the `LoginComponent`, you'll add an *object* as the +second argument in the `router.navigate` function and provide the `preserveQueryParams` and `preserveFragment` to pass along the current query parameters and fragment to the next route. Since you'll be navigating to the *Admin Dashboard* route after logging in, you'll update it to handle the @@ -2434,11 +2579,11 @@ query parameters and fragment. *Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service. Just like *route parameters*, the query parameters and fragments are provided as an `Observable`. The updated *Crisis Admin* component feeds the `Observable` directly into the template using the `AsyncPipe`. -Following the steps in this process, you can click on the *Admin* button, that takes you to the *Login* +Now, you can click on the *Admin* button, which takes you to the *Login* page with the provided `query params` and `fragment`. After you click the login button, notice that you have been redirected to the `Admin Dashboard` page with the `query params` and `fragment` still intact. -You can use these persistent bits of information for things that need to be provided with across pages interaction like +You can use these persistent bits of information for things that need to be provided across pages like authentication tokens or session ids. The `query params` and `fragment` can also be preserved using a `RouterLink` with @@ -2446,26 +2591,30 @@ the `preserveQueryParams` and `preserveFragment` bindings respectively. ## Milestone 6: Asynchronous routing -As you have completed the milestones, the application has naturally gotten larger. -As you continue to build out feature areas, the overall application size will get larger also. +As you've worked through the milestones, the application has naturally gotten larger. +As you continue to build out feature areas, the overall application size will continue to grow. At some point you'll reach a tipping point where the application takes long time to load. -How do you combat this problem? With asynchronous routing which loads feature modules _lazily_, on request. +How do you combat this problem? With asynchronous routing, which loads feature modules _lazily_, on request. Lazy loading has multiple benefits. * You can load feature areas only when requested by the user. * You can speed up load time for users that only visit certain areas of the application. -* You can continue expanding lazy-loaded feature areas without increasing the size of the initial load bundle. +* You can continue expanding lazy loaded feature areas without increasing the size of the initial load bundle. You're already made part way there. -By organizing the application into modules — -`AppModule`, `HeroesModule`, `AdminModule` and `CrisisCenterModule` — you have natural candidates for lazy-loading. +By organizing the application into modules—`AppModule`, +`HeroesModule`, `AdminModule` and `CrisisCenterModule`—you +have natural candidates for lazy loading. Some modules, like `AppModule`, must be loaded from the start. -But other can and should be lazy-loaded. -The `AdminModule`, for example, is needed by a few, authorized users, -You should only load it when requested by the right people. -### Lazy-Loading route configuration +But others can and should be lazy loaded. +The `AdminModule`, for example, is needed by a few authorized users, so +you should only load it when requested by the right people. + + +{@a lazy-loading-route-config} +### Lazy Loading route configuration Change the `admin` **path** in the `admin-routing.module.ts` from `'admin'` to an empty string, `''`, the _empty path_. @@ -2495,12 +2644,12 @@ using another bundling tool, such as Webpack, you would use the Webpack mechanis Take the final step and detach the admin feature set from the main application. The root `AppModule` must neither load nor reference the `AdminModule` or its files. -In the `app.module.ts`, remove the `AdminModule` import statement from the top of the file +In `app.module.ts`, remove the `AdminModule` import statement from the top of the file and remove the `AdminModule` from the Angular module's `imports` array. -

    - CanLoad Guard: guarding unauthorized loading of feature modules -

    + +{@a can-load-guard} +### _CanLoad_ Guard: guarding unauthorized loading of feature modules You're already protecting the `AdminModule` with a `CanActivate` guard that prevents unauthorized users from accessing the admin feature area. @@ -2511,17 +2660,19 @@ Ideally, you'd only load the `AdminModule` if the user is logged in. Add a **`CanLoad`** guard that only loads the `AdminModule` once the user is logged in _and_ attempts to access the admin feature area. -The existing _`AuthGuard`_ already has the essential logic, in its `checkLogin` method, to support the `CanLoad` guard. +The existing `AuthGuard` already has the essential logic in +its `checkLogin()` method to support the `CanLoad` guard. -Open the `auth-guard.service.ts`. -Import the `CanLoad` interface from '@angular/router'. +Open `auth-guard.service.ts`. +Import the `CanLoad` interface from `@angular/router`. Add it to the `AuthGuard` class's `implements` list. Then implement `canLoad` as follows: -The router sets the `canLoad` methods `route` parameter to the intended destination URL. -The `checkLogin` method redirects to that URL once the user has logged in. +The router sets the `canLoad()` method's `route` parameter to the intended destination URL. +The `checkLogin()` method redirects to that URL once the user has logged in. -Now import the `AuthGuard` into the `AppRoutingModule` and add the `AuthGuard` to the `canLoad` array for the `admin` route. -The completed admin route looks like this. +Now import the `AuthGuard` into the `AppRoutingModule` and add the `AuthGuard` to the `canLoad` +!{_array} for the `admin` route. +The completed admin route looks like this: {@example 'router/ts/src/app/app-routing.module.5.ts' region='admin'} @@ -2529,12 +2680,12 @@ The completed admin route looks like this. {@a preloading} -### _Preloading_: background loading of feature areas +### Preloading: background loading of feature areas You've learned how to load modules on-demand. You can also load modules asynchronously with _preloading_. This may seem like what the app has been doing all along. Not quite. -The `AppModule` for instance is loaded when the application starts; that's _eager_ loading. +The `AppModule` is loaded when the application starts; that's _eager_ loading. Now the `AdminModule` loads only when the user clicks on a link; that's _lazy_ loading. _Preloading_ is something in between. @@ -2552,22 +2703,28 @@ By the time the user navigates to the _Crisis Center_, its module will have been That's _preloading_. -#### How it works + +{@a how-preloading} +#### How preloading works After each _successful_ navigation, the router looks in its configuration for an unloaded module that it can preload. -Whether it preloads a module and which modules it preloads depends upon the *preload strategy*. +Whether it preloads a module, and which modules it preloads, depends upon the *preload strategy*. The `Router` offers two preloading strategies out of the box: * No preloading at all which is the default. Lazy loaded feature areas are still loaded on demand. * Preloading of all lazy loaded feature areas. -Out of the box, the router either never preloads, or preloads every lazy-load module. -The `Router` also supports [custom preloading strategies](#custom-preloading) for fine control over which modules to preload and when. +Out of the box, the router either never preloads, or preloads every lazy load module. +The `Router` also supports [custom preloading strategies](#custom-preloading) for +fine control over which modules to preload and when. -In this next section, you'll update the `CrisisCenterModule` to load lazily by default and use the `PreloadAllModules` strategy +In this next section, you'll update the `CrisisCenterModule` to load lazily +by default and use the `PreloadAllModules` strategy to load it (and _all other_ lazy loaded modules) as soon as possible. + +{@a lazy-load-crisis-center} #### Lazy load the _crisis center_ Update the route configuration to lazy load the `CrisisCenterModule`. @@ -2608,9 +2765,9 @@ To enable preloading of all lazy loaded modules, import the `PreloadAllModules` The second argument in the `RouterModule.forRoot` method takes an object for additional configuration options. The `preloadingStrategy` is one of those options. -Add the `PreloadAllModules` token to the `forRoot` call:This tells the `Router` preloader to immediately load _all_ lazy-loaded routes (routes with a `loadChildren` property). +Add the `PreloadAllModules` token to the `forRoot` call:This tells the `Router` preloader to immediately load _all_ lazy loaded routes (routes with a `loadChildren` property). -When you visit `http://localhost:3000`, the `/heroes` route loads immediately upon launch. +When you visit `http://localhost:3000`, the `/heroes` route loads immediately upon launch and the router starts loading the `CrisisCenterModule` right after the `HeroesModule` loads. Surprisingly, the `AdminModule` does _not_ preload. Something is blocking it. @@ -2622,11 +2779,11 @@ Surprisingly, the `AdminModule` does _not_ preload. Something is blocking it. The `PreloadAllModules` strategy does not load feature areas protected by a [CanLoad](#can-load-guard) guard. This is by design. -You added a `canLoad` guard to the route to the `AdminModule` a few steps back +You added a `CanLoad` guard to the route in the `AdminModule` a few steps back to block loading of that module until the user is authorized. -That `canLoad` guard takes precedence over the preload strategy. +That `CanLoad` guard takes precedence over the preload strategy. -If you want both to preload a module and guard against unauthorized access, +If you want to preload a module _and_ guard against unauthorized access, drop the `canLoad` guard and rely on the [CanActivate](#can-activate-guard) guard alone. @@ -2646,7 +2803,7 @@ Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModul Add a new file to the project called `selective-preloading-strategy.ts` and define a `SelectivePreloadingStrategy` service class as follows:`SelectivePreloadingStrategy` implements the `PreloadingStrategy`, which has one method, `preload`. -The router calls the `preload` method with two arguments +The router calls the `preload` method with two arguments: 1. The route to consider. 1. A loader function that can load the routed module asynchronously. @@ -2668,7 +2825,7 @@ But first, make a few changes to the `AppRoutingModule`. elsewhere in the app. Now edit the `AdminDashboardComponent` to display the log of preloaded routes. -1. Import the `SelectivePreloadingStrategy` (it's a service) +1. Import the `SelectivePreloadingStrategy` (it's a service). 1. Inject it into the dashboard's constructor. 1. Update the template to display the strategy service's `preloadedModules` array. @@ -2694,12 +2851,15 @@ to see the finished route configuration. {@a final-app} -## Wrap Up +## Wrap up and final app You've covered a lot of ground in this guide and the application is too big to reprint here. -Please visit the and +Please visit the where you can download the final source code. + +{@a appendices} + ## Appendices The balance of this guide is a set of appendices that @@ -2707,27 +2867,30 @@ elaborate some of the points you covered quickly above. The appendix material isn't essential. Continued reading is for the curious. -## Appendix: Link parameters array -A link parameters array holds the ingredients for router navigation: +{@a link-parameters-array} -* the *path* of the route to the destination component -* required and optional route parameters that go into the route URL +## Appendix: link parameters array + +A link parameters array holds the following ingredients for router navigation: + +* The *path* of the route to the destination component. +* Required and optional route parameters that go into the route URL. You can bind the `RouterLink` directive to such an array like this: -You've written a two element array when specifying a route parameter like this +You've written a two element array when specifying a route parameter like this: You can provide optional route parameters in an object like this: These three examples cover the need for an app with one level routing. The moment you add a child router, such as the crisis center, you create new link array possibilities. -Recall that you specified a default child route for crisis center so this simple `RouterLink` is fine. +Recall that you specified a default child route for the crisis center so this simple `RouterLink` is fine. Parse it out. * The first item in the array identifies the parent route (`/crisis-center`). * There are no parameters for this parent route so you're done with it. * There is no default for the child route so you need to pick one. -* You're navigating to the `CrisisListComponent`, whose route path is `/`, but you don't need to explicitly add the slash -* Voila! `['/crisis-center']`. +* You're navigating to the `CrisisListComponent`, whose route path is `/`, but you don't need to explicitly add the slash. +* Voilà! `['/crisis-center']`. Take it a step further. Consider the following router link that navigates from the root of the application down to the *Dragon Crisis*: @@ -2740,11 +2903,14 @@ navigates from the root of the application down to the *Dragon Crisis*: If you wanted to, you could redefine the `AppComponent` template with *Crisis Center* routes exclusively: In sum, you can write applications with one, two or more levels of routing. The link parameters array affords the flexibility to represent any routing depth and -any legal sequence of route paths, (required) router parameters and (optional) route parameter objects. +any legal sequence of route paths, (required) router parameters, and (optional) route parameter objects. {@a browser-url-styles} + +{@a location-strategy} + ## Appendix: *LocationStrategy* and browser URL styles When the router navigates to a new component view, it updates the browser's location and history @@ -2752,23 +2918,23 @@ with a URL for that view. This is a strictly local URL. The browser shouldn't send this URL to the server and should not reload the page. -Modern HTML 5 browsers support +Modern HTML5 browsers support history.pushState, a technique that changes a browser's location and history without triggering a server page request. The router can compose a "natural" URL that is indistinguishable from one that would otherwise require a page load. -Here's the *Crisis Center* URL in this "HTML 5 pushState" style: +Here's the *Crisis Center* URL in this "HTML5 pushState" style: localhost:3002/crisis-center/ -Older browsers send page requests to the server when the location URL changes ... -unless the change occurs after a "#" (called the "hash"). +Older browsers send page requests to the server when the location URL changes +_unless_ the change occurs after a "#" (called the "hash"). Routers can take advantage of this exception by composing in-application route -URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center* +URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center*. localhost:3002/src/#/crisis-center/ @@ -2777,22 +2943,22 @@ URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center* The router supports both styles with two `LocationStrategy` providers: -1. `PathLocationStrategy` - the default "HTML 5 pushState" style. -1. `HashLocationStrategy` - the "hash URL" style. +1. `PathLocationStrategy`—the default "HTML5 pushState" style. +1. `HashLocationStrategy`—the "hash URL" style. The `RouterModule.forRoot` function sets the `LocationStrategy` to the `PathLocationStrategy`, making it the default strategy. You can switch to the `HashLocationStrategy` with an override during the bootstrapping process if you prefer it. -Learn about "providers" and the bootstrap process in the -[Dependency Injection guide](dependency-injection.html#bootstrap) +Learn about providers and the bootstrap process in the +[Dependency Injection guide](dependency-injection.html#bootstrap). ### Which strategy is best? You must choose a strategy and you need to make the right call early in the project. It won't be easy to change later once the application is in production and there are lots of application URL references in the wild. -Almost all Angular projects should use the default HTML 5 style. +Almost all Angular projects should use the default HTML5 style. It produces URLs that are easier for users to understand. And it preserves the option to do _server-side rendering_ later. @@ -2808,27 +2974,27 @@ without hashes (#) in the middle. Stick with the default unless you have a compelling reason to resort to hash routes. -### HTML 5 URLs and the *<base href>* +### HTML5 URLs and the *<base href>* While the router uses the -HTML 5 pushState -style by default, you *must* configure that strategy with a **base href** +HTML5 pushState +style by default, you *must* configure that strategy with a **base href**. The preferred way to configure the strategy is to add a <base href> element tag in the `` of the `index.html`. Without that tag, the browser may not be able to load resources -(images, css, scripts) when "deep linking" into the app. +(images, CSS, scripts) when "deep linking" into the app. Bad things could happen when someone pastes an application link into the -browser's address bar or clicks such a link in an email link. +browser's address bar or clicks such a link in an email. Some developers may not be able to add the `` element, perhaps because they don't have access to `` or the `index.html`. -Those developers may still use HTML 5 URLs by taking two remedial steps: +Those developers may still use HTML5 URLs by taking two remedial steps: 1. Provide the router with an appropriate [APP_BASE_HREF][] value. -1. Use _root URLs_ for all web resources: css, images, scripts, and template html files. +1. Use _root URLs_ for all web resources: CSS, images, scripts, and template HTML files. [APP_BASE_HREF]: ../api/common/index/APP_BASE_HREF-let.html ### *HashLocationStrategy* diff --git a/aio/content/guide/security.md b/aio/content/guide/security.md index a40b9efb48..657bcee945 100644 --- a/aio/content/guide/security.md +++ b/aio/content/guide/security.md @@ -2,7 +2,7 @@ Security @intro -Developing for content security in Angular applications +Developing for content security in Angular applications. @description This page describes Angular's built-in diff --git a/aio/content/guide/server-communication.md b/aio/content/guide/server-communication.md index e68fb06d56..f65ddab885 100644 --- a/aio/content/guide/server-communication.md +++ b/aio/content/guide/server-communication.md @@ -13,27 +13,50 @@ it isn't covered in this page.Modern browsers support two HTTP-based APIs: [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs. -This page covers: - -- [The Tour of Heroes *HTTP* client demo](#http-client). -- [Fetch data with http.get](#fetch-data). -
  • [RxJS library](#rxjs).
  • -
  • [Enable RxJS operators](#enable-rxjs-operators).
  • -- [Process the response object](#extract-data). -- [Always handle errors](#error-handling). -- [Send data to the server](#update). -
  • [Fall back to promises](#promises).
  • -- [Cross-Origin Requests: Wikipedia example](#cors). -
      -
    • [Search parameters](#search-parameters).
    • -
    • [More fun with observables](#more-observables).
    • +# Contents +* [Demos](#demos) +* [Providing HTTP Services](#http-providers) +* [The Tour of Heroes *HTTP* client demo](#http-client) + - [The `HeroListComponent` class](#HeroListComponent) +* [Fetch data with `http.get()`](#fetch-data) +
    • [RxJS library](#rxjs-library) +
        +
      • [Enable RxJS operators](#enable-rxjs-operators)
      -- [Guarding against Cross-Site Request Forgery](#xsrf). -- [Override default request headers (and other request options)](#override-default-request-options). -- [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api). +
    • +* [Process the response object](#extract-data) + - [Parse to `JSON`](#parse-to-json) + - [Do not return the response object](#no-return-response-object) + - [Always handle errors](#error-handling) + - [`HeroListComponent` error handling](#hero-list-component) +* [Send data to the server](#update) + - [Headers](#headers) + - [JSON results](#json-results) + +
      • [Fall back to promises](#promises)
      + +* [Cross-Origin Requests: Wikipedia example](#cors) +
        +
      • [Search Wikipedia](#search-wikipedia)
      • +
      • [Search parameters](#search-parameters)
      • +
      • [The WikiComponent](#wikicomponent)
      • +
      +* [A wasteful app](#wasteful-app) +
    • [More fun with Observables](#more-observables) +
        +
      • [Create a stream of search terms](#create-stream)
      • +
      • [Listen for search terms](#listen-for-search-terms)
      • +
      +
    • +* [Guarding against Cross-Site Request Forgery](#xsrf) +* [Override default request headers (and other request options)](#override-default-request-options) +* [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api) A live example illustrates these topics. + +{@a demos} + # Demos This page describes server communication with the help of the following demos: @@ -58,7 +81,7 @@ Register providers by importing other NgModules to the root NgModule in `app.mod The `HttpModule` is necessary for making HTTP calls. -Though the JsonpModule isn't necessary for plain HTTP, +Though the `JsonpModule` isn't necessary for plain HTTP, there is a JSONP demo later in this page. Loading its module now saves time. ## The Tour of Heroes HTTP client demo @@ -116,7 +139,7 @@ Components are easier to test and debug when their constructors are simple, and {@a HeroService} -## Fetch data with http.get +## Fetch data with _http.get()_ In many of the previous samples the app faked the interaction with the server by returning mock heroes in a service like this one: @@ -149,13 +172,15 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U {@a extract-data} ## Process the response object -Remember that the `getHeroes()` method used an `!{_priv}extractData` helper method to map the `!{_priv}http.get` response object to heroes: +Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes: {@example 'server-communication/ts/src/app/toh/hero.service.ts' region='extract-data'} The `response` object doesn't hold the data in a form the app can use directly. You must parse the response data into a JSON object. + +{@a parse-to-json} ### Parse to JSON Don't expect the decoded JSON to be the heroes !{_array} directly. This server always wraps JSON results in an object with a `data` @@ -169,8 +194,12 @@ This is conventional web API behavior, driven by Make no assumptions about the server API. Not all servers return an object with a `data` property. + ~~~ + + +{@a no-return-response-object} ### Do not return the response object The `getHeroes()` method _could_ have returned the HTTP response but this wouldn't be a best practice. @@ -225,7 +254,7 @@ just the name of a new hero and returns an `Observable` of `Hero`. It begins lik To implement it, you must know the server's API for creating heroes. -[This sample's data server](#server) follows typical REST guidelines. +[This sample's data server](#in-mem-web-api) follows typical REST guidelines. It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request at the same endpoint as `GET` heroes. It expects the new hero data to arrive in the body of the request, @@ -240,14 +269,20 @@ The server generates the `id` and returns the entire `JSON` representation of the new hero including its generated id. The hero arrives tucked inside a response object with its own `data` property. -Now that you know how the API works, implement `addHero()`as follows: +Now that you know how the API works, implement `addHero()` as follows: {@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'} + + +{@a headers} ### Headers In the `headers` object, the `Content-Type` specifies that the body represents JSON. + + +{@a json-results} ### JSON results As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](#extract-data) @@ -270,10 +305,13 @@ This is called the [same-origin policy](https://en.wikipedia.org/wiki/Same-origi Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol. -If the server requires user credentials, you'll enable them in the [request headers](#headers). +If the server requires user credentials, enable them in the [request headers](#headers). Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP). Wikipedia is one such server. -This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.### Search wikipedia +This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP. + +{@a search-wikipedia} +### Search Wikipedia Here is a simple search that shows suggestions from Wikipedia as the user types in a text box: @@ -289,7 +327,7 @@ types in a text box: ## Guarding against Cross-Site Request Forgery In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting -a different web page with malignant code that secretly sends a malicious request to your application's web server, +a different web page with malignant code that secretly sends a malicious request to your application's web server. The server and client application must work together to thwart this attack. Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests. @@ -311,7 +349,7 @@ Request options (such as headers) are merged into the before the request is processed. The `HttpModule` provides these default options via the `RequestOptions` token. -You can override these defaults to suit your application needs. +You can override these defaults to suit your application needs by creating a custom sub-class of `RequestOptions` that sets the default options for the application. @@ -326,7 +364,7 @@ Then it registers the provider in the root `AppModule`. {@example 'server-communication/ts/src/app/app.module.ts' region='provide-default-request-options'} -Remember to include this provider during setup when unit testing the app's HTTP services.After this change, the `header` option setting in `HeroService.addHero` is no longer necessary, +Remember to include this provider during setup when unit testing the app's HTTP services.After this change, the `header` option setting in `HeroService.addHero()` is no longer necessary, {@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'} diff --git a/aio/content/cookbook/set-document-title.md b/aio/content/guide/set-document-title.md similarity index 100% rename from aio/content/cookbook/set-document-title.md rename to aio/content/guide/set-document-title.md diff --git a/aio/content/guide/setup.md b/aio/content/guide/setup.md index 615dd0093c..2846faec04 100644 --- a/aio/content/guide/setup.md +++ b/aio/content/guide/setup.md @@ -2,7 +2,7 @@ Setup for local development @intro -Install the Angular QuickStart seed for faster, more efficient development on your machine +Install the Angular QuickStart seed for faster, more efficient development on your machine. @description diff --git a/aio/content/guide/structural-directives.md b/aio/content/guide/structural-directives.md index 6ab512b18f..014e6b0948 100644 --- a/aio/content/guide/structural-directives.md +++ b/aio/content/guide/structural-directives.md @@ -19,15 +19,15 @@ how you can write your own structural directives to do the same thing. - [What are structural directives?](#definition) - [*NgIf* case study](#ngIf) -- [Group sibling elements with <ng-container>](#ng-container) -- [The asterisk (\*) prefix](#asterisk) -- [Inside *NgFor*](#ngfor) +- [The asterisk (*) prefix](#asterisk) +- [Inside *NgFor*](#ngFor) - [microsyntax](#microsyntax) - [template input variables](#template-input-variable) - [one structural directive per element](#one-per-element) - [Inside the *NgSwitch* directives](#ngSwitch) -- [Prefer the (\*) prefix](#prefer-asterisk) +- [Prefer the (*) prefix](#prefer-asterisk) - [The <template> element](#template) +- [Group sibling elements with <ng-container>](#ng-container) - [Write a structural directive](#unless) Try the . @@ -45,15 +45,13 @@ As with other directives, you apply a structural directive to a _host element_. The directive then does whatever it's supposed to do with that host element and its descendents. Structural directives are easy to recognize. -An asterisk (\*) precedes the directive attribute name as in this example. - -{@example 'structural-directives/ts/src/app/app.component.html' region='ngif'} - +An asterisk (*) precedes the directive attribute name as in this example. No brackets. No parentheses. Just `*ngIf` set to a string. -You'll learn in this guide that the [asterisk (\*) is a convenience notation](#asterisk) -and the string is a [_microsyntax_](#microsyntax) rather than the usual [template expression](template-syntax.html#template-expressions). -Angular "de-sugars" this notation into a marked-up `