refactor(docs-infra): remove linenums=false since it is now the default (#31674)

PR Close #31674
This commit is contained in:
George Kalpakas 2019-07-20 20:40:17 +03:00 committed by Miško Hevery
parent dd0be7feb7
commit 1bcd58cee8
82 changed files with 1257 additions and 2097 deletions

View File

@ -66,7 +66,7 @@ The following table lists some of the key AngularJS template features with their
### Bindings/interpolation
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation"></code-example>
In Angular, a template expression in curly braces still denotes one-way binding.
@ -102,7 +102,7 @@ The following table lists some of the key AngularJS template features with their
### Pipes
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase"></code-example>
In Angular you use similar syntax with the pipe (|) character to filter output, but now you call them **pipes**.
@ -136,7 +136,7 @@ The following table lists some of the key AngularJS template features with their
### Input variables
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local"></code-example>
Angular has true template input variables that are explicitly defined using the `let` keyword.
@ -202,10 +202,10 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bootstrapping
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts"></code-example>
<br>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts"></code-example>
Angular doesn't have a bootstrap directive.
@ -245,7 +245,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngClass
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass"></code-example>
In Angular, the `ngClass` directive works similarly.
@ -291,7 +291,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `click` event
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding"></code-example>
AngularJS event-based directives do not exist in Angular.
@ -338,7 +338,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Component decorator
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
In Angular, the template no longer specifies its associated controller.
@ -401,7 +401,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `href` property
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href"></code-example>
Angular uses property binding; there is no built-in *href* directive.
@ -412,7 +412,7 @@ The following are some of the key AngularJS built-in directives and their equiva
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example.
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link"></code-example>
For more information on routing, see the [RouterLink binding](guide/router#router-link)
@ -445,7 +445,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### *ngIf
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf"></code-example>
The `*ngIf` directive in Angular works the same as the `ng-if` directive in AngularJS. It removes
@ -480,7 +480,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngModel
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel"></code-example>
In Angular, **two-way binding** is denoted by `[()]`, descriptively referred to as a "banana in a box". This syntax is a shortcut for defining both property binding (from the component to the view)
@ -516,7 +516,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### *ngFor
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor"></code-example>
The `*ngFor` directive in Angular is similar to the `ng-repeat` directive in AngularJS. It repeats
@ -559,7 +559,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `hidden` property
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden"></code-example>
Angular uses property binding; there is no built-in *show* directive.
@ -598,7 +598,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `src` property
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src"></code-example>
Angular uses property binding; there is no built-in *src* directive.
@ -635,7 +635,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngStyle
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle"></code-example>
In Angular, the `ngStyle` directive works similarly. It sets a CSS style on an HTML element based on an expression.
@ -690,7 +690,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngSwitch
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch"></code-example>
In Angular, the `ngSwitch` directive works similarly.
@ -765,7 +765,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### currency
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency"></code-example>
The Angular `currency` pipe is similar although some of the parameters have changed.
@ -793,7 +793,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### date
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date"></code-example>
The Angular `date` pipe is similar.
@ -847,7 +847,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### json
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json"></code-example>
The Angular `json` pipe does the same thing.
@ -876,7 +876,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### slice
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice"></code-example>
The `SlicePipe` does the same thing but the *order of the parameters is reversed*, in keeping
@ -907,7 +907,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### lowercase
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase"></code-example>
The Angular `lowercase` pipe does the same thing.
@ -935,7 +935,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### number
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number"></code-example>
The Angular `number` pipe is similar.
@ -1068,7 +1068,7 @@ The Angular code is shown using TypeScript.
### NgModules
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts"></code-example>
NgModules, defined with the `NgModule` decorator, serve the same purpose:
@ -1109,7 +1109,7 @@ The Angular code is shown using TypeScript.
### Component decorator
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
Angular adds a decorator to the component class to provide any required metadata.
@ -1145,7 +1145,7 @@ The Angular code is shown using TypeScript.
### Component class
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></code-example>
In Angular, you create a component class.
@ -1184,7 +1184,7 @@ The Angular code is shown using TypeScript.
### Dependency injection
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di"></code-example>
In Angular, you pass in dependencies as arguments to the component class constructor.
@ -1254,7 +1254,7 @@ also encapsulate a style sheet within a specific component.
### Styles configuration
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles"></code-example>
With the Angular CLI, you can configure your global styles in the `angular.json` file.
You can rename the extension to `.scss` to use sass.
@ -1263,7 +1263,7 @@ also encapsulate a style sheet within a specific component.
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
a style sheet for a particular component.
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url"></code-example>
This allows you to set appropriate styles for individual components that wont leak into

View File

@ -37,8 +37,7 @@ To get started with adding Angular animations to your project, import the animat
Import `BrowserAnimationsModule`, which introduces the animation capabilities into your Angular root application module.
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript"></code-example>
<div class="alert is-helpful">
@ -180,9 +179,7 @@ In this example, we'll name the trigger `openClose`, and attach it to the `butto
Animations are defined in the metadata of the component that controls the HTML element to be animated. Put the code that defines your animations under the `animations:` property within the `@Component()` decorator.
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript"
region="component" linenums="false">
</code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript" region="component"></code-example>
When you've defined an animation trigger for a component, you can attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an `@` symbol. Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, where `triggerName` is the name of the trigger, and `expression` evaluates to a defined animation state.

View File

@ -21,7 +21,7 @@ Angular offers two ways to compile your application:
1. **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime.
1. **_Ahead-of-Time_ (AOT)**, which compiles your app at build time.
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
<code-example language="sh" class="code-shell">
ng build
@ -945,7 +945,7 @@ import { calculateValue } from './utilities';
To correct this error, export a function from the module and refer to the function in a `useFactory` provider instead.
<code-example linenums="false">
<code-example>
// CORRECTED
import { calculateValue } from './utilities';
@ -978,7 +978,7 @@ The compiler does not support references to variables assigned by [destructuring
For example, you cannot write something like this:
<code-example linenums="false">
<code-example>
// ERROR
import { configuration } from './configuration';
@ -994,7 +994,7 @@ const {foo, bar} = configuration;
To correct this error, refer to non-destructured values.
<code-example linenums="false">
<code-example>
// CORRECTED
import { configuration } from './configuration';
...
@ -1041,7 +1041,7 @@ you can finesse the problem in four steps:
Here's an illustrative example.
<code-example linenums="false">
<code-example>
// CORRECTED
import { Inject } from '@angular/core';
@ -1064,7 +1064,7 @@ uses the `@Inject(WINDOW)` to generate the injection code.
Angular does something similar with the `DOCUMENT` token so you can inject the browser's `document` object (or an abstraction of it, depending upon the platform in which the application runs).
<code-example linenums="false">
<code-example>
import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';
@ -1101,7 +1101,7 @@ that you referenced in metadata.
The compiler can understand simple enum values but not complex values such as those derived from computed properties.
<code-example linenums="false">
<code-example>
// ERROR
enum Colors {
Red = 1,
@ -1311,7 +1311,7 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
{@a tsconfig-extends}
## Configuration inheritance with extends
Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the `tsconfig.json` on `angularCompilerOptions`. A tsconfig file can inherit configurations from another file using the `extends` property.
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
The configuration from the base file are loaded first, then overridden by those in the inheriting config file.
Example:
```json

View File

@ -10,7 +10,7 @@ Learn more in [The App Shell Model](https://developers.google.com/web/fundamenta
## Step 1: Prepare the application
You can do this with the following CLI command:
<code-example format="." language="bash" linenums="false">
<code-example format="." language="bash">
ng new my-app --routing
</code-example>
@ -20,7 +20,7 @@ For an existing application, you have to manually add the `RouterModule` and def
Use the CLI to automatically create the app shell.
<code-example format="." language="bash" linenums="false">
<code-example format="." language="bash">
ng generate app-shell --client-project my-app --universal-project server-app
</code-example>
@ -29,7 +29,7 @@ ng generate app-shell --client-project my-app --universal-project server-app
After running this command you will notice that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
@ -52,7 +52,7 @@ After running this command you will notice that the `angular.json` configuration
Use the CLI to build the `app-shell` target.
<code-example format="." language="bash" linenums="false">
<code-example format="." language="bash">
ng run my-app:app-shell
</code-example>

View File

@ -10,12 +10,12 @@ For example, individual components define and control each of the following view
You define a component's application logic&mdash;what it does to support the view&mdash;inside a class.
The class interacts with the view through an API of properties and methods.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
The service is provided to the component through the dependency injection system.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
@ -31,7 +31,7 @@ In addition to containing or pointing to the template, the `@Component` metadata
Here's an example of basic metadata for `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
This example shows some of the most useful `@Component` configuration options:
@ -40,7 +40,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
## Templates and views
@ -69,7 +69,7 @@ This template uses typical HTML elements like `<h2>` and `<p>`, and also includ
* The `*ngFor` directive tells Angular to iterate over a list.
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
@ -87,7 +87,7 @@ The following diagram shows the four forms of data binding markup. Each form has
This example from the `HeroListComponent` template uses three of these forms.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
displays the component's `hero.name` property value within the `<li>` element.
@ -97,11 +97,11 @@ displays the component's `hero.name` property value within the `<li>` element.
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
In two-way binding, a data property value flows to the input box from the component as with property binding.
The user's changes also flow back to the component, resetting the property to the latest value,
@ -151,20 +151,20 @@ Angular templates are *dynamic*. When Angular renders them, it transforms the DO
A component is technically a directive.
However, components are so distinctive and central to Angular applications that Angular
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
template-oriented features.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
#### Structural directives
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
The example template uses two built-in structural directives to add application logic to how the view is rendered.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
@ -176,7 +176,7 @@ In templates they look like regular HTML attributes, hence the name.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
Angular has more pre-defined directives that either alter the layout structure
(for example, [ngSwitch](guide/template-syntax#ngSwitch))

View File

@ -23,7 +23,7 @@ An NgModule is defined by a class decorated with `@NgModule()`. The `@NgModule()
Here's a simple root NgModule definition.
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts" linenums="false"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts"></code-example>
<div class="alert is-helpful">
@ -69,9 +69,9 @@ In JavaScript each *file* is a module and all objects defined in the file belong
The module declares some objects to be public by marking them with the `export` key word.
Other JavaScript modules use *import statements* to access public objects from other modules.
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="imports"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="export"></code-example>
<div class="alert is-helpful">
<a href="http://exploringjs.com/es6/ch_modules.html">Learn more about the JavaScript module system on the web.</a>
@ -87,17 +87,17 @@ Angular loads as a collection of JavaScript modules. You can think of them as li
For example, import Angular's `Component` decorator from the `@angular/core` library like this.
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
<code-example path="architecture/src/app/app.component.ts" region="import"></code-example>
You also import NgModules from Angular *libraries* using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library.
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module"></code-example>
In the example of the simple root module above, the application module needs material from within
`BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports"></code-example>
In this way you're using the Angular and JavaScript module systems *together*. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.

View File

@ -1,7 +1,7 @@
# Introduction to services and dependency injection
*Service* is a broad category encompassing any value, function, or feature that an app needs.
A service is typically a class with a narrow, well-defined purpose.
A service is typically a class with a narrow, well-defined purpose.
It should do something specific and do it well.
Angular distinguishes components from services to increase modularity and reusability.
@ -14,9 +14,9 @@ in order to mediate between the view (rendered by the template)
and the application logic (which often includes some notion of a *model*).
A component can delegate certain tasks to services, such as fetching data from the server,
validating user input, or logging directly to the console.
validating user input, or logging directly to the console.
By defining such processing tasks in an *injectable service class*, you make those tasks
available to any component.
available to any component.
You can also make your app more adaptable by injecting different providers of the same kind of service,
as appropriate in different circumstances.
@ -28,21 +28,21 @@ available to components through *dependency injection*.
Here's an example of a service class that logs to the browser console.
<code-example path="architecture/src/app/logger.service.ts" linenums="false" header="src/app/logger.service.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/logger.service.ts" header="src/app/logger.service.ts (class)" region="class"></code-example>
Services can depend on other services. For example, here's a `HeroService` that depends on the `Logger` service, and also uses `BackendService` to get heroes. That service in turn might depend on the `HttpClient` service to fetch heroes asynchronously from a server.
<code-example path="architecture/src/app/hero.service.ts" linenums="false" header="src/app/hero.service.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/hero.service.ts" header="src/app/hero.service.ts (class)" region="class"></code-example>
## Dependency injection (DI)
<img src="generated/images/guide/architecture/dependency-injection.png" alt="Service" class="left">
DI is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
* The *injector* is the main mechanism. Angular creates an application-wide injector for you during the bootstrap process, and additional injectors as needed. You don't have to create injectors.
@ -50,19 +50,19 @@ Similarly, use the `@Injectable()` decorator to indicate that a component or oth
* A *provider* is an object that tells an injector how to obtain or create a dependency.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For a service, the provider is typically the service class itself.
<div class="alert is-helpful">
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
</div>
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types. For example, the constructor of `HeroListComponent` needs `HeroService`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
@ -78,26 +78,26 @@ The process of `HeroService` injection looks something like this.
You must register at least one *provider* of any service you are going to use.
The provider can be part of the service's own metadata, making that service available everywhere,
or you can register providers with specific modules or components.
or you can register providers with specific modules or components.
You register providers in the metadata of the service (in the `@Injectable()` decorator),
or in the `@NgModule()` or `@Component()` metadata
or in the `@NgModule()` or `@Component()` metadata
* By default, the Angular CLI command [`ng generate service`](cli/generate) registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
```
```
@Injectable({
providedIn: 'root',
})
```
```
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService`
and injects it into any class that asks for it.
and injects it into any class that asks for it.
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
by removing the service from the compiled app if it isn't used.
by removing the service from the compiled app if it isn't used.
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
```
```
@NgModule({
providers: [
BackendService,
@ -105,12 +105,12 @@ or in the `@NgModule()` or `@Component()` metadata
],
...
})
```
```
* When you register a provider at the component level, you get a new instance of the
service with each new instance of that component.
service with each new instance of that component.
At the component level, register a service provider in the `providers` property of the `@Component()` metadata.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
For more detailed information, see the [Dependency Injection](guide/dependency-injection) section.

View File

@ -37,13 +37,13 @@ This page demonstrates building a simple _appHighlight_ attribute
directive to set an element's background color
when the user hovers over that element. You can apply it like this:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (applied)" region="applied"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (applied)" region="applied"></code-example>
{@a write-directive}
Please note that directives _do not_ support namespaces.
<code-example path="attribute-directives/src/app/app.component.avoid.html" linenums="false" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
### Write the directive code
@ -101,7 +101,7 @@ Now edit the generated `src/app/highlight.directive.ts` to look as follows:
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
You use the `ElementRef` in the directive's constructor
to [inject](guide/dependency-injection) a reference to the host DOM element,
to [inject](guide/dependency-injection) a reference to the host DOM element,
the element to which you applied `appHighlight`.
`ElementRef` grants direct access to the host DOM element
@ -140,12 +140,12 @@ and respond by setting or clearing the highlight color.
Begin by adding `HostListener` to the list of imported symbols.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` decorator.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
The `@HostListener` decorator lets you subscribe to events of the DOM
element that hosts an attribute directive, the `<p>` in this case.
@ -166,7 +166,7 @@ The handlers delegate to a helper method that sets the color on the host DOM ele
The helper method, `highlight`, was extracted from the constructor.
The revised constructor simply declares the injected `el: ElementRef`.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
Here's the updated directive in full:
@ -187,11 +187,11 @@ Currently the highlight color is hard-coded _within_ the directive. That's infle
In this section, you give the developer the power to set the highlight color while applying the directive.
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Add a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
{@a input}
@ -204,19 +204,19 @@ Without that input metadata, Angular rejects the binding; see [below](guide/attr
Try it by adding the following directive binding variations to the `AppComponent` template:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
Add a `color` property to the `AppComponent`.
<code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
Let it control the highlight color with a property binding.
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
The `[appHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
and sets the directive's highlight color with a property binding.
@ -225,7 +225,7 @@ That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
This is disagreeable. The word, `appHighlight`, is a terrible property name and it doesn't convey the property's intent.
@ -237,23 +237,23 @@ Fortunately you can name the directive property whatever you want _and_ **_alias
Restore the original property name and specify the selector as the alias in the argument to `@Input`.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
_Inside_ the directive the property is known as `highlightColor`.
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
Here's the latest version of the directive class.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (excerpt)"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (excerpt)"></code-example>
## Write a harness to try it
@ -263,11 +263,11 @@ lets you pick the highlight color with a radio button and bind your color choice
Update <code>app.component.html</code> as follows:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (v2)" region="v2"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (v2)" region="v2"></code-example>
Revise the `AppComponent.color` so that it has no initial value.
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
<code-example path="attribute-directives/src/app/app.component.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
Here are the harness and directive in action.
@ -287,12 +287,12 @@ Let the template developer set the default color.
Add a second **input** property to `HighlightDirective` called `defaultColor`:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
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.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
How do you bind to a second property when you're already binding to the `appHighlight` attribute name?
@ -300,7 +300,7 @@ As with components, you can add as many directive property bindings as you need
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.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
because you made it _public_ with the `@Input` decorator.
@ -342,11 +342,11 @@ You can also experience and download the <live-example title="Attribute Directiv
In this demo, the `highlightColor` property is an ***input*** property of
the `HighlightDirective`. You've seen it applied without an alias:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
You've seen it with an alias:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
Either way, the `@Input` decorator tells Angular that this property is
_public_ and available for binding by a parent component.
@ -378,7 +378,7 @@ You can tell if `@Input` is needed by the position of the property name in a bin
Now apply that reasoning to the following example:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
* The `color` property in the expression on the right belongs to the template's component.
The template and its component trust each other.

View File

@ -106,19 +106,16 @@ To use a directive, component, or pipe in a module, you must do a few things:
Those three steps look like the following. In the file where you create your directive, export it.
The following example, named `ItemDirective` is the default directive structure that the CLI generates in its own file, `item.directive.ts`:
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts" linenums="false">
</code-example>
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts"></code-example>
The key point here is that you have to export it so you can import it elsewhere. Next, import it
into the NgModule, in this example `app.module.ts`, with a JavaScript import statement:
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts"></code-example>
And in the same file, add it to the `@NgModule` `declarations` array:
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts"></code-example>
Now you could use your `ItemDirective` in a component. This example uses `AppModule`, but you'd do it the same way for a feature module. For more about directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives). You'd also use the same technique for [pipes](guide/pipes) and components.

View File

@ -527,7 +527,7 @@ If you are not using the CLI, add your polyfill scripts directly to the host web
For example:
<code-example header="src/index.html" language="html" linenums="false">
<code-example header="src/index.html" language="html">
&lt;!-- pre-zone polyfills -->
&lt;script src="node_modules/core-js/client/shim.min.js">&lt;/script>
&lt;script src="node_modules/web-animations-js/web-animations.min.js">&lt;/script>

View File

@ -191,7 +191,7 @@ For our example builder, we expect the `options` value to be a `JsonObject` with
We can provide the following schema for type validation of these values.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
"$schema": "http://json-schema.org/schema",
@ -222,7 +222,7 @@ To link our builder implementation with its schema and name, we need to create a
Create a file named `builders.json` file that looks like this.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
"builders": {
@ -238,7 +238,7 @@ Create a file named `builders.json` file that looks like this.
In the `package.json` file, add a `builders` key that tells the Architect tool where to find our builder definition file.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
"name": "@example/command-runner",
@ -257,7 +257,7 @@ The first part of this is the package name (resolved using node resolution), an
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
<code-example format="." language="typescript" linenums="false">
<code-example format="." language="typescript">
context.reportStatus(`Executing "${options.command}"...`);
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
@ -274,7 +274,7 @@ The Architect tool uses the target definition to resolve input options for a giv
The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'.
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
"myApp": {
...
@ -361,7 +361,7 @@ npm install @example/command-runner
If we create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
// ...
@ -413,7 +413,7 @@ We need to update the `angular.json` file to add a target for this builder to th
* The configurations key is optional, we'll leave it out for now.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
{
"projects": {
@ -495,7 +495,7 @@ Use integration testing for your builder, so that you can use the Architect sche
Heres an example of a test that runs the command builder.
The test uses the builder to run the `ls` command, then validates that it ran successfully and listed the proper files.
<code-example format="." language="typescript" linenums="false">
<code-example format="." language="typescript">
import { Architect, ArchitectHost } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
@ -592,4 +592,4 @@ The CLI Builder API provides a new way of changing the behavior of the Angular C
* We recommend that you use integration tests to test Architect builders. You can use unit tests to validate the logic that the builder executes.
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.

View File

@ -38,7 +38,7 @@ The following example demonstrates how to use `query()` and `stagger()` function
* Animate each element on screen for 0.5 seconds using a custom-defined easing curve, simultaneously fading it in and un-transforming it.
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript" linenums="false"></code-example>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript"></code-example>
## Parallel animation using group() function
@ -51,7 +51,7 @@ You've seen how to add a delay between each successive animation. But you may al
In the following example, using groups on both `:enter` and `:leave` allow for two different timing configurations. They're applied to the same element in parallel, but run independently.
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript" linenums="false"></code-example>
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript"></code-example>
## Sequential vs. parallel animations
@ -74,7 +74,7 @@ The HTML template contains a trigger called `filterAnimation`.
The component file contains three transitions.
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript" linenums="false"></code-example>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript"></code-example>
The animation does the following:
@ -101,4 +101,4 @@ You may also be interested in the following:
* [Introduction to Angular animations](guide/animations)
* [Transition and triggers](guide/transition-and-triggers)
* [Reusable animations](guide/reusable-animations)
* [Route transition animations](guide/route-animations)
* [Route transition animations](guide/route-animations)

View File

@ -21,8 +21,7 @@ One way to do this is to set the `styles` property in the component metadata.
The `styles` property takes an array of strings that contain CSS code.
Usually you give it one string, as in the following example:
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts" linenums="false">
</code-example>
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts"></code-example>
## Style scope
@ -71,8 +70,7 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css" linenums="false">
</code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css"></code-example>
The `:host` selector is the only way to target the host element. You can't reach
the host element from inside the component with other selectors because it's not part of the
@ -83,8 +81,7 @@ including another selector inside parentheses after `:host`.
The next example targets the host element again, but only when it also has the `active` CSS class.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css" linenums="false">
</code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css"></code-example>
### :host-context
@ -99,8 +96,7 @@ up to the document root. The `:host-context()` selector is useful when combined
The following example applies a `background-color` style to all `<h2>` elements *inside* the component, only
if some ancestor element has the CSS class `theme-light`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css" linenums="false">
</code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css"></code-example>
### (deprecated) `/deep/`, `>>>`, and `::ng-deep`
@ -115,9 +111,7 @@ can bleed into other components.
The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM.
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css" linenums="false">
</code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css"></code-example>
The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
@ -304,8 +298,7 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts" linenums="false">
</code-example>
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
`ShadowDom` view encapsulation only works on browsers that have native support
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the

View File

@ -21,18 +21,14 @@ constructor, and lets the framework provide them.
The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`.
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts"></code-example>
`UserContext` in turn depends on both `LoggerService` and
`UserService`, another service that gathers information about a particular user.
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)"></code-example>
When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`.
@ -185,17 +181,13 @@ This `HeroBiosAndContactsComponent` is a revision of `HeroBiosComponent` which y
Focus on the template:
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts"></code-example>
Now there's a new `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)"></code-example>
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
@ -212,9 +204,7 @@ Here's `HeroContactComponent`, which demonstrates the qualifying decorators.
Focus on the constructor parameters.
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts"></code-example>
The `@Host()` function decorating the `heroCache` constructor property ensures that
you get a reference to the cache service from the parent `HeroBioComponent`.
@ -299,9 +289,7 @@ whose `nativeElement` property exposes the DOM element for the directive to mani
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
first without a value (yielding the default color) and then with an assigned color value.
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)"></code-example>
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
@ -325,9 +313,7 @@ Angular passes this token to the injector and assigns the result to the paramete
The following is a typical example.
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)"></code-example>
Angular asks the injector for the service associated with `LoggerService`
@ -386,9 +372,7 @@ You can also use a value provider in a unit test to provide mock data in place o
The `HeroOfTheMonthComponent` example has two value providers.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than
requiring the injector to create a new instance with `new` or use its own cached instance.
@ -427,9 +411,7 @@ extend the default class, or emulate the behavior of the real class in a test ca
The following code shows two examples in `HeroOfTheMonthComponent`.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
The first provider is the *de-sugared*, expanded form of the most typical case in which the
class to be created (`HeroService`) is also the provider's dependency injection token.
@ -448,9 +430,7 @@ Components outside the tree continue to receive the original `LoggerService` ins
`DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts"></code-example>
{@a useexisting}
@ -472,15 +452,11 @@ You might want to shrink that API surface to just the members you actually need.
In this example, the `MinimalLogger` [class-interface](#class-interface) reduces the API to two members:
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts"></code-example>
The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent`.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)"></code-example>
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
@ -532,9 +508,7 @@ The `runnersUpFactory()` returns the *provider factory function*, which can use
the passed-in state value and the injected services `Hero` and `HeroService`.
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)"></code-example>
The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object,
the string of names.
@ -578,9 +552,7 @@ as the token for a provider of `LoggerService`.
`MinimalLogger` is an abstract class.
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
An abstract class is usually a base class that you can extend.
In this app, however there is no class that inherits from `MinimalLogger`.
@ -606,9 +578,7 @@ Using a class as an interface gives you the characteristics of an interface in a
To minimize memory cost, however, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function.
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
Notice that it doesn't have any members. It never grows no matter how many members you add to the class,
as long as those members are typed but not implemented.
@ -635,15 +605,11 @@ another token that happens to have the same name.
You encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
You created the `TITLE` token like this:
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
@ -733,9 +699,7 @@ appear *above* the class definition.
Break the circularity with `forwardRef`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
<!--- Waiting for good examples

View File

@ -2,16 +2,16 @@
Application components often need to share information.
You can often use loosely coupled techniques for sharing information,
such as data binding and service sharing,
but sometimes it makes sense for one component to have a direct reference to another component.
such as data binding and service sharing,
but sometimes it makes sense for one component to have a direct reference to another component.
You need a direct reference, for instance, to access values or call methods on that component.
Obtaining a component reference is a bit tricky in Angular.
Angular components themselves do not have a tree that you can
Angular components themselves do not have a tree that you can
inspect or navigate programmatically. The parent-child relationship is indirect,
established through the components' [view objects](guide/glossary#view).
Each component has a *host view*, and can have additional *embedded views*.
Each component has a *host view*, and can have additional *embedded views*.
An embedded view in component A is the
host view of component B, which can in turn have embedded view.
This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component,
@ -40,18 +40,14 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)"></code-example>
*Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor:
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)"></code-example>
@ -98,17 +94,13 @@ inject its parent via the parent's base class*.
The sample's `CraigComponent` explores this question. [Looking back](#alex),
you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)"></code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)"></code-example>
@ -138,9 +130,7 @@ and add that provider to the `providers` array of the `@Component()` metadata fo
{@a alex-providers}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
[Parent](#parent-token) is the provider's class interface token.
@ -149,9 +139,7 @@ The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the c
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
the same way you've done it before.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)"></code-example>
@ -177,9 +165,7 @@ That means he must both *inject* the `Parent` class interface to get *Alice* and
Here's *Barry*.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)"></code-example>
@ -229,9 +215,7 @@ You [learned earlier](guide/dependency-injection-in-action#class-interface) that
The example defines a `Parent` class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)"></code-example>
@ -241,9 +225,7 @@ Such a narrow interface helps decouple the child component class from its parent
A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)"></code-example>
@ -251,9 +233,7 @@ Doing so adds clarity to the code. But it's not technically necessary.
Although `AlexComponent` has a `name` property, as required by its `Base` class,
its class signature doesn't mention `Parent`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)"></code-example>
@ -277,21 +257,15 @@ It doesn't in this example *only* to demonstrate that the code will compile and
Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref).
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
You can extract that logic into a helper function like the following.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
Now you can add a simpler, more meaningful parent provider to your components.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
You can do better. The current version of the helper function can only alias the `Parent` class interface.
@ -299,14 +273,10 @@ The application might have a variety of parent types, each with its own class in
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
And here's how you could use it with a different parent type.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>

View File

@ -68,13 +68,11 @@ using the `Logger` token.
Another class, `EvenBetterLogger`, might display the user name in the log message.
This logger gets the user from an injected `UserService` instance.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger"></code-example>
The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5"></code-example>
{@a aliased-class-providers}
@ -92,13 +90,11 @@ when a component asks for either the new or the old logger.
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a"></code-example>
To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b"></code-example>
{@a value-provider}
@ -110,13 +106,11 @@ configure the injector with the `useValue` option
The following code defines a variable that creates such an object to play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger"></code-example>
The following provider object uses the `useValue` key to associate the variable with the `Logger` token.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7"></code-example>
{@a non-class-dependencies}
@ -130,8 +124,7 @@ like the title of the application or the address of a web API endpoint.
These configuration objects aren't always instances of a class.
They can be object literals, as shown in the following example.
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)"></code-example>
{@a interface-not-valid-token}
@ -141,11 +134,9 @@ The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
Unfortunately, you cannot use a TypeScript interface as a token.
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface"></code-example>
<div class="alert is-helpful">
@ -163,22 +154,19 @@ Another solution to choosing a provider token for non-class dependencies is
to define and use an `InjectionToken` object.
The following example shows how to define such a token.
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts"></code-example>
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
Register the dependency provider using the `InjectionToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9"></code-example>
Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject()` parameter decorator.
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts"></code-example>
<div class="alert is-helpful">
@ -215,22 +203,19 @@ who is authorized and who isn't.
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>
You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`.
A factory provider needs a factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
Although `HeroService` has no access to `UserService`, the factory function does.
You inject both `Logger` and `UserService` into the factory provider
and let the injector pass them along to the factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`.
@ -322,13 +307,13 @@ Thus, services in the NgModule `providers` array or at component level are not t
The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule.
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts"></code-example>
You can then import this module into your application module
to make the service available for injection in your app,
as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts"></code-example>
When `ngc` runs, it compiles `AppModule` into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services.
@ -340,11 +325,11 @@ You can make a provider tree-shakable by specifying it in the `@Injectable()` de
The following example shows the tree-shakable equivalent to the `ServiceModule` example above.
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts"></code-example>
The service can be instantiated by configuring a factory function, as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts"></code-example>
<div class="alert is-helpful">

View File

@ -196,8 +196,7 @@ Listing dependencies as constructor parameters may be all you need to test appli
For example, you can create a new `HeroListComponent` with a mock service that you can manipulate
under test.
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts"></code-example>
<div class="alert is-helpful">
@ -259,8 +258,7 @@ In simple examples, the dependency value is an *instance*, and
the class *type* serves as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts"></code-example>
The behavior is similar when you write a constructor that requires an injected class-based dependency.
When you define a constructor parameter with the `HeroService` class type,
@ -287,8 +285,7 @@ constructor parameter with `@Optional()`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="import-optional">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor"></code-example>
When using `@Optional()`, your code must be prepared for a null value. If you
don't register a logger provider anywhere, the injector sets the

View File

@ -175,7 +175,7 @@ modified to serve `index.html`:
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
[here](http://stackoverflow.com/a/26152011/2116927):
<code-example format='.' language="xml" linenums="false">
<code-example format='.' language="xml">
&lt;system.webServer&gt;
&lt;rewrite&gt;
&lt;rules&gt;
@ -436,7 +436,7 @@ When you create a production build using [`ng build --prod`](cli/build), the CLI
The `index.html` file is also modified during the build process to include script tags that enable differential loading. See the sample output below from the `index.html` file produced during a build using `ng build`.
<code-example language="html" format="." linenums="false">
<code-example language="html" format=".">
&lt;body>
&lt;app-root>&lt;/app-root>
&lt;script src="runtime-es2015.js" type="module">&lt;/script>
@ -479,7 +479,7 @@ not IE 9-11 # For IE 9-11 support, remove 'not'.
The `tsconfig.json` looks like this:
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
{
"compileOnSave": false,
@ -550,7 +550,7 @@ To maintain the benefits of differential loading, however, a better option is to
To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `tsconfig.app.json` with the following content.
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
{
"extends": "./tsconfig.app.json",
@ -563,7 +563,7 @@ To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `t
In `angular.json` add two new configuration sections under the `build` and `serve` targets to point to the new TypeScript configuration.
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
"build": {
"builder": "@angular-devkit/build-angular:browser",
@ -610,7 +610,7 @@ ng serve --configuration es5
Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with the following content.
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
{
"extends": "./tsconfig.spec.json",
@ -621,7 +621,7 @@ Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with th
</code-example>
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
"test": {
"builder": "@angular-devkit/build-angular:karma",
@ -649,7 +649,7 @@ ng test --configuration es5
Create an [ES5 serve configuration](guide/deployment#configuring-serve-for-es5) as explained above, and configuration an ES5 configuration for the E2E target.
<code-example language="json" format="." linenums="false">
<code-example language="json" format=".">
"test": {
"builder": "@angular-devkit/build-angular:protractor",

View File

@ -507,7 +507,7 @@ public createAndAppendElement() {
**After:**
<code-example linenums=false>
<code-example>
public createAndAppendElement() {
const el = __ngRendererCreateElement(this.renderer, this.element, 'span');

View File

@ -31,7 +31,7 @@ The easiest way to display a component property
is to bind the property name through interpolation.
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
Delete the <code>app.component.html</code> file. It is not needed for this example.
@ -41,9 +41,7 @@ changing the template and the body of the component.
When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts">
</code-example>
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
@ -53,9 +51,7 @@ The template displays the two component properties using double curly brace
interpolation:
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
@ -97,9 +93,7 @@ The CSS `selector` in the `@Component` decorator specifies an element named `<ap
That element is a placeholder in the body of your `index.html` file:
<code-example path="displaying-data/src/index.html" linenums="false" header="src/index.html (body)" region="body">
</code-example>
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
@ -133,7 +127,7 @@ is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties.
<div class="alert is-helpful">
By default, the Angular CLI command [`ng generate component`](cli/generate) generates components with a template file. You can override that with:
<code-example hideCopy language="sh" class="code-shell">
@ -148,9 +142,7 @@ In either style, the template data bindings have the same access to the componen
Although this example uses variable assignment to initialize the components, you could instead declare and initialize the properties using a constructor:
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" linenums="false" region="class">
</code-example>
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
@ -163,9 +155,7 @@ This app uses more terse "variable assignment" style simply for brevity.
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (class)" region="class">
</code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
@ -173,9 +163,7 @@ Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
@ -184,9 +172,7 @@ in the `<li>` element is the Angular "repeater" directive.
It marks that `<li>` element (and its children) as the "repeater template":
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (li)" region="li">
</code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
@ -251,9 +237,7 @@ of hero names into an array of `Hero` objects. For that you'll need a `Hero` cla
With the following code:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts">
</code-example>
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
@ -265,9 +249,7 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts (id)" region="id">
</code-example>
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
@ -285,9 +267,7 @@ After importing the `Hero` class, the `AppComponent.heroes` property can return
of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (heroes)" region="heroes">
</code-example>
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
@ -296,9 +276,7 @@ At the moment it displays the hero's `id` and `name`.
Fix that to display only the hero's `name` property.
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
@ -316,9 +294,7 @@ The Angular `ngIf` directive inserts or removes an element based on a _truthy/fa
To see it in action, add the following paragraph at the bottom of the template:
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (message)" region="message">
</code-example>
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>

View File

@ -665,7 +665,7 @@ Examine the `src/app/app.component.ts` file which defines two nested _#docregion
The inner, `class-skeleton` region appears twice, once to capture the code that opens the class definition and once to capture the code that closes the class definition.
<code-example linenums="false">
<code-example>
// #docplaster
...
// #docregion class, class-skeleton

View File

@ -35,9 +35,7 @@ The ad banner uses a helper directive called `AdDirective` to
mark valid insertion points in the template.
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts" linenums="false">
</code-example>
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts"></code-example>
@ -62,9 +60,7 @@ To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
where to dynamically load components.
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)" linenums="false">
</code-example>
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)"></code-example>
@ -91,9 +87,7 @@ With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdI
and loads a new component every 3 seconds by calling `loadComponent()`.
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)" linenums="false">
</code-example>
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)"></code-example>
@ -150,9 +144,7 @@ dynamically loaded components since they load at runtime.
To ensure that the compiler still generates a factory,
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)" linenums="false">
</code-example>
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)"></code-example>

View File

@ -77,18 +77,14 @@ appropriate controls dynamically.
via the `type` property.
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts" linenums="false">
</code-example>
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example>
`DropdownQuestion` presents a list of choices in a select box.
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts" linenums="false">
</code-example>
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
@ -97,9 +93,7 @@ In a nutshell, the form group consumes the metadata from the question model and
allows you to specify default values and validation rules.
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts" linenums="false">
</code-example>
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
{@a form-component}

View File

@ -66,8 +66,7 @@ ng generate component customer-dashboard/CustomerDashboard
This generates a folder for the new component within the customer-dashboard folder and updates the feature module with the `CustomerDashboardComponent` info:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
</code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
@ -77,8 +76,7 @@ The `CustomerDashboardComponent` is now in the JavaScript import list at the top
To incorporate the feature module into your app, you have to let the root module, `app.module.ts`, know about it. Notice the `CustomerDashboardModule` export at the bottom of `customer-dashboard.module.ts`. This exposes it so that other modules can get to it. To import it into the `AppModule`, add it to the imports in `app.module.ts` and to the `imports` array:
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts"></code-example>
Now the `AppModule` knows about the feature module. If you were to add any service providers to the feature module, `AppModule` would know about those too, as would any other feature modules. However, NgModules dont expose their components.
@ -88,21 +86,18 @@ Now the `AppModule` knows about the feature module. If you were to add any servi
When the CLI generated the `CustomerDashboardComponent` for the feature module, it included a template, `customer-dashboard.component.html`, with the following markup:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" linenums="false">
</code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html"></code-example>
To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. In `customer-dashboard.module.ts`, just beneath the `declarations` array, add an `exports` array containing `CustomerDashboardComponent`:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
</code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
Next, in the `AppComponent`, `app.component.html`, add the tag `<app-customer-dashboard>`:
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:

View File

@ -4,7 +4,7 @@ You develop applications in the context of an Angular [workspace](guide/glossary
The Angular CLI `ng new` command creates a workspace.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng new &lt;my-project&gt;
</code-example>
@ -111,7 +111,7 @@ An `e2e/` folder at the top level contains source files for a set of end-to-end
For a multi-project workspace, application-specific end-to-end tests are in the project root, under `projects/project-name/e2e/`.
<code-example language="none" linenums="false">
<code-example language="none">
e2e/
src/ (end-to-end tests for my-app)
app.e2e-spec.ts
@ -131,13 +131,13 @@ A multi-project workspace is suitable for an enterprise that uses a single repos
If you intend to have multiple projects in a workspace, you can skip the initial application generation when you create the workspace, and give the workspace a unique name.
The following command creates a workspace with all of the workspace-wide configuration files, but no root-level application.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng new my-workspace --createApplication="false"
</code-example>
You can then generate apps and libraries with names that are unique within the workspace.
<code-example language="bash" linenums="false">
<code-example language="bash">
cd my-workspace
ng generate application my-first-app
</code-example>
@ -148,7 +148,7 @@ The first explicitly generated application goes into the `projects/` folder alon
Newly generated libraries are also added under `projects/`.
When you create projects this way, the file structure of the workspace is entirely consistent with the structure of the [workspace configuration file](guide/workspace-config), `angular.json`.
<code-example language="none" linenums="false">
<code-example language="none">
my-workspace/
... (workspace-wide config files)
projects/ (generated applications and libraries)

View File

@ -29,9 +29,7 @@ either a list of validation errors, which results in an INVALID status, or null,
You can then inspect the control's state by exporting `ngModel` to a local template variable.
The following example exports `NgModel` into a variable called `name`:
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)"></code-example>
Note the following:
@ -92,8 +90,7 @@ built-in validators&mdash;this time, in function form. See below:
{@a reactive-component-class}
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
Note that:
@ -106,8 +103,7 @@ for the template.
If you look at the template for the name input again, it is fairly similar to the template-driven example.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)"></code-example>
Key takeaways:
@ -125,8 +121,7 @@ Consider the `forbiddenNameValidator` function from previous
[examples](guide/form-validation#reactive-component-class) in
this guide. Here's what the definition of that function looks like:
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
@ -148,8 +143,7 @@ at which point the form uses the last value emitted for validation.
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
to the `FormControl`.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
### Adding to template-driven forms
@ -161,8 +155,7 @@ The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `
Angular recognizes the directive's role in the validation process because the directive registers itself
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validators.
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></code-example>
The directive class then implements the `Validator` interface, so that it can easily integrate
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
@ -173,9 +166,7 @@ comes together:
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)" linenums="false">
</code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)"></code-example>
<div class="alert is-helpful">
@ -245,8 +236,7 @@ const heroForm = new FormGroup({
The validator code is as follows:
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></code-example>
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
@ -255,8 +245,7 @@ First we retrieve the child controls by calling the `FormGroup`'s [get](api/form
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html" linenums="false">
</code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html"></code-example>
Note that we check if:
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
@ -265,16 +254,13 @@ Note that we check if:
### Adding to template driven forms
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></code-example>
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html" linenums="false">
</code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html"></code-example>
To provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html" linenums="false">
</code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html"></code-example>
Note that we check if:
- the form has the cross validation error returned by the `identityRevealed` validator,
@ -313,7 +299,7 @@ To validate the potential alter ego, we need to consult a central database of al
Let's start by creating the validator class.
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:

View File

@ -109,9 +109,7 @@ Using the Angular CLI command [`ng generate class`](cli/generate), generate a ne
With this content:
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts">
</code-example>
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts"></code-example>
It's an anemic model with few requirements and no behavior. Perfect for the demo.
@ -122,9 +120,7 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
You can create a new hero like this:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" region="SkyDog">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
## Create a form component
@ -142,9 +138,7 @@ Using the Angular CLI command [`ng generate component`](cli/generate), generate
With this content:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
Theres nothing special about this component, nothing form-specific,
nothing to distinguish it from any component you've written before.
@ -176,9 +170,7 @@ Because template-driven forms are in their own module, you need to add the `Form
Update it with the following:
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts">
</code-example>
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts"></code-example>
<div class="alert is-helpful">
@ -204,9 +196,7 @@ Update it with the following:
Replace the contents of its template with the following:
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html">
</code-example>
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -221,9 +211,7 @@ Replace the contents of its template with the following:
Update the template file with the following contents:
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html"></code-example>
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
@ -259,9 +247,7 @@ Bootstrap gives the form a little style.
To add the stylesheet, open `styles.css` and add the following import line at the top:
<code-example path="forms/src/styles.1.css" linenums="false" header="src/styles.css">
</code-example>
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
## Add powers with _*ngFor_
@ -274,9 +260,7 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
Add the following HTML *immediately below* the *Alter Ego* group:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (powers)" region="powers">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (powers)" region="powers"></code-example>
This code repeats the `<option>` tag for each power in the list of powers.
The `pow` template input variable is a different power in each iteration;
@ -307,9 +291,7 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
<div class="alert is-helpful">
@ -325,9 +307,7 @@ You need one more addition to display the data. Declare
a template variable for the form. Update the `<form>` tag with
`#heroForm="ngForm"` as follows:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable"></code-example>
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
@ -391,9 +371,7 @@ Then you can confirm that two-way data binding works *for the entire hero model*
After revision, the core of the form should look like this:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
<div class="alert is-helpful">
@ -493,9 +471,7 @@ You can leverage those class names to change the appearance of the control.
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2"></code-example>
Now run the app and look at the _Name_ input box.
Follow these steps *precisely*:
@ -535,15 +511,11 @@ on the left of the input box:
You achieve this effect by adding these class definitions to a new `forms.css` file
that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css">
</code-example>
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css"></code-example>
Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" linenums="false" header="src/index.html (styles)" region="styles">
</code-example>
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
## Show and hide validation error messages
@ -564,9 +536,7 @@ To achieve this effect, extend the `<input>` tag with the following:
Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg"></code-example>
You need a template reference variable to access the input box's Angular control from within the template.
Here you created a variable called `name` and gave it the value "ngModel".
@ -583,9 +553,7 @@ Here you created a variable called `name` and gave it the value "ngModel".
You control visibility of the name error message by binding properties of the `name`
control to the message `<div>` element's `hidden` property.
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"></code-example>
In this example, you hide the message when the control is valid or pristine;
"pristine" means the user hasn't changed the value since it was displayed in this form.
@ -609,13 +577,9 @@ power to valid values.
Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)">
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)"></code-example>
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)" linenums="false">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)"></code-example>
Run the application again, click the *New Hero* button, and the form clears.
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
@ -634,9 +598,7 @@ Replacing the hero object *did not restore the pristine state* of the form contr
You have to clear all of the flags imperatively, which you can do
by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)"></code-example>
Now clicking "New Hero" resets both the form and its control flags.
@ -651,9 +613,7 @@ A "form submit" is useless at the moment.
To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit"></code-example>
You'd already defined a template reference variable,
`#heroForm`, and initialized it with the value "ngForm".
@ -664,9 +624,7 @@ You'll bind the form's overall validity via
the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button"></code-example>
If you run the application now, you find that the button is enabled&mdash;although
it doesn't do anything useful yet.
@ -703,17 +661,13 @@ hide the data entry area and display something else.
Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div"></code-example>
The main form is visible from the start because the
`submitted` property is false until you submit the form,
as this fragment from the `HeroFormComponent` shows:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted"></code-example>
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
as planned.
@ -721,9 +675,7 @@ as planned.
Now the app needs to show something else while the form is in the submitted state.
Add the following HTML below the `<div>` wrapper you just wrote:
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted">
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted"></code-example>
There's the hero again, displayed read-only with interpolation bindings.
This `<div>` appears only while the component is in the submitted state.

View File

@ -763,7 +763,7 @@ NgModules are delivered within scoped packages whose names begin with the Angula
Import a scoped package in the same way that you import a normal package.
<code-example path="architecture/src/app/app.component.ts" linenums="false" header="architecture/src/app/app.component.ts (import)" region="import">
<code-example path="architecture/src/app/app.component.ts" header="architecture/src/app/app.component.ts (import)" region="import">
</code-example>

View File

@ -19,7 +19,7 @@ an NgModule, directive-level injectors follow the structure of the component hie
The choices you make about where to configure providers lead to differences in the final bundle size, service _scope_, and service _lifetime_.
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
@ -29,12 +29,12 @@ You're likely to inject `UserService` in many places throughout the app and will
<header>Platform injector</header>
When you use `providedIn:'root'`, you are configuring the root injector for the _app_, which is the injector for `AppModule`.
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
This allows multiple apps to share a platform configuration. For example, a browser has only one URL bar, no matter how many apps you have running.
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
Learn more about dependency resolution through the injector hierarchy:
Learn more about dependency resolution through the injector hierarchy:
[What you always wanted to know about Angular Dependency Injection tree](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
@ -42,24 +42,24 @@ Learn more about dependency resolution through the injector hierarchy:
*NgModule-level* providers can be specified with `@NgModule()` `providers` metadata option, or in the `@Injectable()` `providedIn` option (with some module other than the root `AppModule`).
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
For both root-level and module-level injectors, a service instance lives for the life of the app or module, and Angular injects this one service instance in every class that needs it.
*Component-level* providers configure each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular can't inject the same service instance anywhere else.
*Component-level* providers configure each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular can't inject the same service instance anywhere else.
A component-provided service may have a limited lifetime.
Each new instance of the component gets its own instance of the service.
A component-provided service may have a limited lifetime.
Each new instance of the component gets its own instance of the service.
When the component instance is destroyed, so is that service instance.
In our sample app, `HeroComponent` is created when the application starts
In our sample app, `HeroComponent` is created when the application starts
and is never destroyed,
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
If you want to restrict `HeroService` access to `HeroComponent` and its nested
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
If you want to restrict `HeroService` access to `HeroComponent` and its nested
`HeroListComponent`, provide `HeroService` at the component level, in `HeroComponent` metadata.
* See more [examples of component-level injection](#component-injectors) below.
@ -67,32 +67,32 @@ If you want to restrict `HeroService` access to `HeroComponent` and its nested
{@a register-providers-injectable}
### @Injectable-level configuration
### @Injectable-level configuration
The `@Injectable()` decorator identifies every service class. The `providedIn` metadata option for a service class configures a specific injector (typically `root`)
to use the decorated class as a provider of the service.
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
to use the decorated class as a provider of the service.
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
The following example configures a provider for `HeroService` using the `@Injectable()` decorator on the class.
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts"></code-example>
This configuration tells Angular that the app's root injector is responsible for creating an
This configuration tells Angular that the app's root injector is responsible for creating an
instance of `HeroService` by invoking its constructor,
and for making that instance available across the application.
and for making that instance available across the application.
Providing a service with the app's root injector is a typical case,
and the CLI sets up this kind of a provider automatically for you
when generating a new service.
when generating a new service.
However, you might not always want to provide your service at the root level.
You might, for instance, want users to explicitly opt-in to using the service.
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
For example, in the following excerpt, the `@Injectable()` decorator configures a provider
that is available in any injector that includes the `HeroModule`.
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts"></code-example>
This is generally no different from configuring the injector of the NgModule itself,
except that the service is tree-shakable if the NgModule doesn't use it.
@ -108,18 +108,16 @@ and leave it up to the app whether to provide the service.
You can configure a provider at the module level using the `providers` metadata option for a non-root NgModule, in order to limit the scope of the provider to that module.
This is the equivalent of specifying the non-root module in the `@Injectable()` metadata, except that the service provided via `providers` is not tree-shakable.
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
However, if you configure a app-wide provider in the `@NgModule()` metadata for `AppModule`,
it overrides one configured for `root` in the `@Injectable()` metadata.
You can do this to configure a non-default provider of a service that is shared with multiple apps.
it overrides one configured for `root` in the `@Injectable()` metadata.
You can do this to configure a non-default provider of a service that is shared with multiple apps.
Here is an example of the case where the component router configuration includes
a non-default [location strategy](guide/router#location-strategy) by listing its provider
in the `providers` list of the `AppModule`.
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)" linenums="false">
</code-example>
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)"></code-example>
{@a register-providers-component}
@ -130,19 +128,18 @@ Individual components within an NgModule have their own injectors.
You can limit the scope of a provider to a component and its children
by configuring the provider at the component level using the `@Component` metadata.
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts"></code-example>
### Element injectors
An injector does not actually belong to a component, but rather to the component instance's anchor element in the DOM. A different component instance on a different DOM element uses a different injector.
Components are a special type of directive, and the `providers` property of
`@Component()` is inherited from `@Directive()`.
`@Component()` is inherited from `@Directive()`.
Directives can also have dependencies, and you can configure providers
in their `@Directive()` metadata.
in their `@Directive()` metadata.
When you configure a provider for a component or directive using the `providers` property, that provider belongs to the injector for the anchor DOM element. Components and directives on the same element share an injector.
<!--- TBD with examples
@ -168,16 +165,16 @@ When a component requests a dependency, Angular tries to satisfy that dependency
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
If that injector can't satisfy the request, it passes the request along to the next parent injector up the tree.
The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors.
If it runs out of ancestors, Angular throws an error.
If it runs out of ancestors, Angular throws an error.
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
<div class="alert is-helpful">
You can cap the bubbling by adding the `@Host()` parameter decorator on the dependant-service parameter
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found.
@ -197,7 +194,7 @@ The guide sample offers some scenarios where you might want to do so.
### Scenario: service isolation
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
For example, the guide sample includes a `VillainsListComponent` that displays a list of villains.
It gets those villains from a `VillainsService`.
@ -207,9 +204,7 @@ that would make the `VillainsService` available everywhere in the application, i
Instead, you can provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" header="src/app/villains-list.component.ts (metadata)" region="metadata">
</code-example>
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" header="src/app/villains-list.component.ts (metadata)" region="metadata"></code-example>
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
@ -273,9 +268,7 @@ Every component would share the same service instance, and each component would
To prevent this, we configure the component-level injector of `HeroTaxReturnComponent` to provide the service, using the `providers` property in the component metadata.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" header="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example>
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" header="src/app/hero-tax-return.component.ts (providers)" region="providers"></code-example>
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
Recall that every component _instance_ has its own injector.

View File

@ -11,7 +11,7 @@ You can run the <live-example></live-example> that accompanies this guide.
<div class="alert is-helpful">
The sample app does not require a data server.
It relies on the
It relies on the
[Angular _in-memory-web-api_](https://github.com/angular/in-memory-web-api/blob/master/README.md),
which replaces the _HttpClient_ module's `HttpBackend`.
The replacement service simulates the behavior of a REST-like backend.
@ -22,50 +22,50 @@ Look at the `AppModule` _imports_ to see how it is configured.
## Setup
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
Most apps do so in the root `AppModule`.
<code-example
<code-example
path="http/src/app/app.module.ts"
region="sketch"
header="app/app.module.ts (excerpt)" linenums="false">
header="app/app.module.ts (excerpt)">
</code-example>
Having imported `HttpClientModule` into the `AppModule`, you can inject the `HttpClient`
into an application class as shown in the following `ConfigService` example.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="proto"
header="app/config/config.service.ts (excerpt)" linenums="false">
header="app/config/config.service.ts (excerpt)">
</code-example>
## Getting JSON data
Applications often request JSON data from the server.
For example, the app might need a configuration file on the server, `config.json`,
Applications often request JSON data from the server.
For example, the app might need a configuration file on the server, `config.json`,
that specifies resource URLs.
<code-example
<code-example
path="http/src/assets/config.json"
header="assets/config.json" linenums="false">
header="assets/config.json">
</code-example>
The `ConfigService` fetches this file with a `get()` method on `HttpClient`.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_1"
header="app/config/config.service.ts (getConfig v.1)" linenums="false">
header="app/config/config.service.ts (getConfig v.1)">
</code-example>
A component, such as `ConfigComponent`, injects the `ConfigService` and calls
the `getConfig` service method.
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v1"
header="app/config/config.component.ts (showConfig v.1)" linenums="false">
header="app/config/config.component.ts (showConfig v.1)">
</code-example>
Because the service method returns an `Observable` of configuration data,
@ -93,12 +93,12 @@ the component, even in simple cases like this one.
The subscribe callback above requires bracket notation to extract the data values.
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v1_callback" linenums="false">
region="v1_callback">
</code-example>
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
The `HttpClient.get()` method parsed the JSON server response into the anonymous `Object` type. It doesn't know what the shape of that object is.
@ -106,48 +106,48 @@ You can tell `HttpClient` the type of the response to make consuming the output
First, define an interface with the correct shape:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="config-interface" linenums="false">
region="config-interface">
</code-example>
Then, specify that interface as the `HttpClient.get()` call's type parameter in the service:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_2"
header="app/config/config.service.ts (getConfig v.2)" linenums="false">
region="getConfig_2"
header="app/config/config.service.ts (getConfig v.2)">
</code-example>
The callback in the updated component method receives a typed data object, which is
easier and safer to consume:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v2"
header="app/config/config.component.ts (showConfig v.2)" linenums="false">
header="app/config/config.component.ts (showConfig v.2)">
</code-example>
### Reading the full response
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
Tell `HttpClient` that you want the full response with the `observe` option:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfigResponse" linenums="false">
region="getConfigResponse">
</code-example>
Now `HttpClient.get()` returns an `Observable` of typed `HttpResponse` rather than just the JSON data.
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="showConfigResponse"
region="showConfigResponse"
header="app/config/config.component.ts (showConfigResponse)"
linenums="false">
>
</code-example>
As you can see, the response object has a `body` property of the correct type.
@ -158,11 +158,11 @@ What happens if the request fails on the server, or if a poor network connection
You _could_ handle in the component by adding a second callback to the `.subscribe()`:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v3"
region="v3"
header="app/config/config.component.ts (showConfig v.3 with error handling)"
linenums="false">
>
</code-example>
It's certainly a good idea to give the user some kind of feedback when data access fails.
@ -180,15 +180,15 @@ Or something could go wrong on the client-side such as a network error that prev
The `HttpClient` captures both kinds of errors in its `HttpErrorResponse` and you can inspect that response to figure out what really happened.
Error inspection, interpretation, and resolution is something you want to do in the _service_,
not in the _component_.
Error inspection, interpretation, and resolution is something you want to do in the _service_,
not in the _component_.
You might first devise an error handler like this one:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="handleError"
header="app/config/config.service.ts (handleError)" linenums="false">
region="handleError"
header="app/config/config.service.ts (handleError)">
</code-example>
Notice that this handler returns an RxJS [`ErrorObservable`](#rxjs) with a user-friendly error message.
@ -198,10 +198,10 @@ even a "bad" one.
Now you take the `Observables` returned by the `HttpClient` methods
and _pipe them through_ to the error handler.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_3"
header="app/config/config.service.ts (getConfig v.3 with error handler)" linenums="false">
region="getConfig_3"
header="app/config/config.service.ts (getConfig v.3 with error handler)">
</code-example>
### `retry()`
@ -215,10 +215,10 @@ The simplest is called `retry()` and it automatically re-subscribes to a failed
_Pipe_ it onto the `HttpClient` method result just before the error handler.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig"
header="app/config/config.service.ts (getConfig with retry)" linenums="false">
region="getConfig"
header="app/config/config.service.ts (getConfig with retry)">
</code-example>
{@a rxjs}
@ -229,17 +229,17 @@ You will encounter more RxJS artifacts as you continue below.
[RxJS](http://reactivex.io/rxjs/) is a library for composing asynchronous and callback-based code
in a _functional, reactive style_.
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
RxJS itself is out-of-scope for this guide. You will find many learning resources on the web.
While you can get by with a minimum of RxJS knowledge, you'll want to grow your RxJS skills over time in order to use `HttpClient` effectively.
If you're following along with these code snippets, note that you must import the RxJS observable and operator symbols that appear in those snippets. These `ConfigService` imports are typical.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="rxjs-imports"
header="app/config/config.service.ts (RxJS imports)" linenums="false">
region="rxjs-imports"
header="app/config/config.service.ts (RxJS imports)">
</code-example>
## Requesting non-JSON data
@ -247,24 +247,24 @@ If you're following along with these code snippets, note that you must import th
Not all APIs return JSON data. In this next example,
a `DownloaderService` method reads a text file from the server
and logs the file contents, before returning those contents to the caller
as an `Observable<string>`.
as an `Observable<string>`.
<code-example
<code-example
path="http/src/app/downloader/downloader.service.ts"
region="getTextFile"
header="app/downloader/downloader.service.ts (getTextFile)" linenums="false">
region="getTextFile"
header="app/downloader/downloader.service.ts (getTextFile)">
</code-example>
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
A `download()` method in the `DownloaderComponent` initiates the request by subscribing to the service method.
<code-example
<code-example
path="http/src/app/downloader/downloader.component.ts"
region="download"
header="app/downloader/downloader.component.ts (download)" linenums="false">
region="download"
header="app/downloader/downloader.component.ts (download)">
</code-example>
## Sending data to the server
@ -279,28 +279,28 @@ The following sections excerpt methods of the sample's `HeroesService`.
### Adding headers
Many servers require extra headers for save operations.
For example, they may require a "Content-Type" header to explicitly declare
For example, they may require a "Content-Type" header to explicitly declare
the MIME type of the request body.
Or perhaps the server requires an authorization token.
The `HeroesService` defines such headers in an `httpOptions` object that will be passed
to every `HttpClient` save method.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="http-options"
header="app/heroes/heroes.service.ts (httpOptions)" linenums="false">
region="http-options"
header="app/heroes/heroes.service.ts (httpOptions)">
</code-example>
### Making a POST request
Apps often POST data to a server. They POST when submitting a form.
Apps often POST data to a server. They POST when submitting a form.
In the following example, the `HeroesService` posts when adding a hero to the database.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="addHero"
header="app/heroes/heroes.service.ts (addHero)" linenums="false">
region="addHero"
header="app/heroes/heroes.service.ts (addHero)">
</code-example>
The `HttpClient.post()` method is similar to `get()` in that it has a type parameter
@ -314,13 +314,13 @@ It takes two more parameters:
Of course it catches errors in much the same manner [described above](#error-details).
The `HeroesComponent` initiates the actual POST operation by subscribing to
The `HeroesComponent` initiates the actual POST operation by subscribing to
the `Observable` returned by this service method.
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="add-hero-subscribe"
header="app/heroes/heroes.component.ts (addHero)" linenums="false">
region="add-hero-subscribe"
header="app/heroes/heroes.component.ts (addHero)">
</code-example>
When the server responds successfully with the newly added hero, the component adds
@ -331,22 +331,22 @@ that hero to the displayed `heroes` list.
This application deletes a hero with the `HttpClient.delete` method by passing the hero's id
in the request URL.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="deleteHero"
header="app/heroes/heroes.service.ts (deleteHero)" linenums="false">
region="deleteHero"
header="app/heroes/heroes.service.ts (deleteHero)">
</code-example>
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
the `Observable` returned by this service method.
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="delete-hero-subscribe"
header="app/heroes/heroes.component.ts (deleteHero)" linenums="false">
region="delete-hero-subscribe"
header="app/heroes/heroes.component.ts (deleteHero)">
</code-example>
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
<div class="alert is-important">
@ -355,9 +355,9 @@ You must call _subscribe()_ or nothing happens. Just calling `HeroesService.dele
</div>
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="delete-hero-no-subscribe" linenums="false">
region="delete-hero-no-subscribe">
</code-example>
{@a always-subscribe}
@ -400,10 +400,10 @@ req.subscribe();
An app will send a PUT request to completely replace a resource with updated data.
The following `HeroesService` example is just like the POST example.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="updateHero"
header="app/heroes/heroes.service.ts (updateHero)" linenums="false">
region="updateHero"
header="app/heroes/heroes.service.ts (updateHero)">
</code-example>
For the reasons [explained above](#always-subscribe), the caller (`HeroesComponent.update()` in this case) must `subscribe()` to the observable returned from the `HttpClient.put()`
@ -427,15 +427,15 @@ You can do more.
You can't directly modify the existing headers within the previous options
object because instances of the `HttpHeaders` class are immutable.
Use the `set()` method instead.
Use the `set()` method instead.
It returns a clone of the current instance with the new changes applied.
Here's how you might update the authorization header (after the old token expired)
Here's how you might update the authorization header (after the old token expired)
before making the next request.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="update-headers" linenums="false">
region="update-headers">
</code-example>
#### URL Parameters
@ -443,9 +443,9 @@ before making the next request.
Adding URL search parameters works a similar way.
Here is a `searchHeroes` method that queries for heroes whose names contain the search term.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="searchHeroes" linenums="false">
region="searchHeroes">
</code-example>
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter. If the term were "foo", the GET request URL would be `api/heroes/?name=foo`.
@ -461,9 +461,9 @@ a search request for a package with that name to the NPM web API.
Here's a pertinent excerpt from the template:
<code-example
<code-example
path="http/src/app/package-search/package-search.component.html"
region="search"
region="search"
header="app/package-search/package-search.component.html (search)">
</code-example>
@ -473,9 +473,9 @@ Sending a request for every keystroke could be expensive.
It's better to wait until the user stops typing and then send a request.
That's easy to implement with RxJS operators, as shown in this excerpt.
<code-example
<code-example
path="http/src/app/package-search/package-search.component.ts"
region="debounce"
region="debounce"
header="app/package-search/package-search.component.ts (excerpt)">
</code-example>
@ -514,7 +514,7 @@ The `switchMap()` operator has three important characteristics.
it cancels that request and sends a new one.
3. It returns service responses in their original request order, even if the
server returns them out of order.
server returns them out of order.
<div class="alert is-helpful">
@ -526,14 +526,14 @@ consider moving it to a utility function or into the `PackageSearchService` itse
### Intercepting requests and responses
_HTTP Interception_ is a major feature of `@angular/common/http`.
_HTTP Interception_ is a major feature of `@angular/common/http`.
With interception, you declare _interceptors_ that inspect and transform HTTP requests from your application to the server.
The same interceptors may also inspect and transform the server's responses on their way back to the application.
Multiple interceptors form a _forward-and-backward_ chain of request/response handlers.
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Without interception, developers would have to implement these tasks _explicitly_
Without interception, developers would have to implement these tasks _explicitly_
for each `HttpClient` method call.
#### Write an interceptor
@ -541,13 +541,12 @@ for each `HttpClient` method call.
To implement an interceptor, declare a class that implements the `intercept()` method of the `HttpInterceptor` interface.
Here is a do-nothing _noop_ interceptor that simply passes the request through without touching it:
<code-example
<code-example
path="http/src/app/http-interceptors/noop-interceptor.ts"
header="app/http-interceptors/noop-interceptor.ts"
linenums="false">
header="app/http-interceptors/noop-interceptor.ts">
</code-example>
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
In this sense, each interceptor is fully capable of handling the request entirely by itself.
Most interceptors inspect the request on the way in and forward the (perhaps altered) request to the `handle()` method of the `next` object which implements the [`HttpHandler`](api/common/http/HttpHandler) interface.
@ -564,22 +563,22 @@ This _no-op_ interceptor simply calls `next.handle()` with the original request
#### The _next_ object
The `next` object represents the next interceptor in the chain of interceptors.
The `next` object represents the next interceptor in the chain of interceptors.
The final `next` in the chain is the `HttpClient` backend handler that sends the request to the server and receives the server's response.
Most interceptors call `next.handle()` so that the request flows through to the next interceptor and, eventually, the backend handler.
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
This is a common middleware pattern found in frameworks such as Express.js.
#### Provide the interceptor
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
Like other services, you must provide the interceptor class before the app can use it.
Because interceptors are (optional) dependencies of the `HttpClient` service,
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
Because interceptors are (optional) dependencies of the `HttpClient` service,
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
Interceptors provided _after_ DI creates the `HttpClient` are ignored.
This app provides `HttpClient` in the app's root injector, as a side-effect of importing the `HttpClientModule` in `AppModule`.
@ -588,35 +587,35 @@ You should provide interceptors in `AppModule` as well.
After importing the `HTTP_INTERCEPTORS` injection token from `@angular/common/http`,
write the `NoopInterceptor` provider like this:
<code-example
<code-example
path="http/src/app/http-interceptors/index.ts"
region="noop-provider" linenums="false">
region="noop-provider">
</code-example>
Note the `multi: true` option.
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
Note the `multi: true` option.
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
that injects an array of values, rather than a single value.
You _could_ add this provider directly to the providers array of the `AppModule`.
However, it's rather verbose and there's a good chance that
However, it's rather verbose and there's a good chance that
you'll create more interceptors and provide them in the same way.
You must also pay [close attention to the order](#interceptor-order)
You must also pay [close attention to the order](#interceptor-order)
in which you provide these interceptors.
Consider creating a "barrel" file that gathers all the interceptor providers into an `httpInterceptorProviders` array, starting with this first one, the `NoopInterceptor`.
<code-example
<code-example
path="http/src/app/http-interceptors/index.ts"
region="interceptor-providers"
header="app/http-interceptors/index.ts" linenums="false">
header="app/http-interceptors/index.ts">
</code-example>
Then import and add it to the `AppModule` _providers array_ like this:
<code-example
<code-example
path="http/src/app/app.module.ts"
region="interceptor-providers"
header="app/app.module.ts (interceptor providers)" linenums="false">
header="app/app.module.ts (interceptor providers)">
</code-example>
As you create new interceptors, add them to the `httpInterceptorProviders` array and
@ -647,8 +646,8 @@ That's because interceptors work at a lower level than those `HttpClient` method
Many interceptors are only concerned with the outgoing request and simply return the event stream from `next.handle()` without modifying it.
But interceptors that examine and modify the response from `next.handle()`
will see all of these events.
But interceptors that examine and modify the response from `next.handle()`
will see all of these events.
Your interceptor should return _every event untouched_ unless it has a _compelling reason to do otherwise_.
#### Immutability
@ -660,39 +659,39 @@ rendering them largely immutable.
They are immutable for a good reason: the app may retry a request several times before it succeeds, which means that the interceptor chain may re-process the same request multiple times.
If an interceptor could modify the original request object, the re-tried operation would start from the modified request rather than the original. Immutability ensures that interceptors see the same request for each try.
TypeScript will prevent you from setting `HttpRequest` readonly properties.
TypeScript will prevent you from setting `HttpRequest` readonly properties.
```javascript
// Typescript disallows the following assignment because req.url is readonly
req.url = req.url.replace('http://', 'https://');
```
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
You can clone and modify the request in a single step as in this example.
<code-example
<code-example
path="http/src/app/http-interceptors/ensure-https-interceptor.ts"
region="excerpt"
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)" linenums="false">
region="excerpt"
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)">
</code-example>
The `clone()` method's hash argument allows you to mutate specific properties of the request while copying the others.
##### The request body
The `readonly` assignment guard can't prevent deep updates and, in particular,
The `readonly` assignment guard can't prevent deep updates and, in particular,
it can't prevent you from modifying a property of a request body object.
```javascript
req.body.name = req.body.name.trim(); // bad idea!
```
If you must mutate the request body, copy it first, change the copy,
If you must mutate the request body, copy it first, change the copy,
`clone()` the request, and set the clone's body with the new body, as in the following example.
<code-example
<code-example
path="http/src/app/http-interceptors/trim-name-interceptor.ts"
region="excerpt"
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)" linenums="false">
region="excerpt"
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)">
</code-example>
##### Clearing the request body
@ -710,21 +709,21 @@ If you set the cloned request body to `null`, Angular knows you intend to clear
#### Set default headers
Apps often use an interceptor to set default headers on outgoing requests.
Apps often use an interceptor to set default headers on outgoing requests.
The sample app has an `AuthService` that produces an authorization token.
Here is its `AuthInterceptor` that injects that service to get the token and
adds an authorization header with that token to every outgoing request:
<code-example
<code-example
path="http/src/app/http-interceptors/auth-interceptor.ts"
header="app/http-interceptors/auth-interceptor.ts">
</code-example>
The practice of cloning a request to set new headers is so common that
The practice of cloning a request to set new headers is so common that
there's a `setHeaders` shortcut for it:
<code-example
<code-example
path="http/src/app/http-interceptors/auth-interceptor.ts"
region="set-header-shortcut">
</code-example>
@ -737,16 +736,16 @@ An interceptor that alters headers can be used for a number of different operati
#### Logging
Because interceptors can process the request and response _together_, they can do things like time and log
an entire HTTP operation.
Because interceptors can process the request and response _together_, they can do things like time and log
an entire HTTP operation.
Consider the following `LoggingInterceptor`, which captures the time of the request,
the time of the response, and logs the outcome with the elapsed time
with the injected `MessageService`.
<code-example
<code-example
path="http/src/app/http-interceptors/logging-interceptor.ts"
region="excerpt"
region="excerpt"
header="app/http-interceptors/logging-interceptor.ts)">
</code-example>
@ -761,20 +760,20 @@ Neither `tap` nor `finalize` touch the values of the observable stream returned
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
For example, you might decide to cache certain requests and responses to improve performance.
You can delegate caching to an interceptor without disturbing your existing data services.
You can delegate caching to an interceptor without disturbing your existing data services.
The `CachingInterceptor` demonstrates this approach.
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="v1"
header="app/http-interceptors/caching-interceptor.ts)" linenums="false">
region="v1"
header="app/http-interceptors/caching-interceptor.ts)">
</code-example>
The `isCachable()` function determines if the request is cachable.
In this sample, only GET requests to the npm package search api are cachable.
If the request is not cachable, the interceptor simply forwards the request
If the request is not cachable, the interceptor simply forwards the request
to the next handler in the chain.
If a cachable request is found in the cache, the interceptor returns an `of()` _observable_ with
@ -783,7 +782,7 @@ the cached response, by-passing the `next` handler (and all other interceptors d
If a cachable request is not in cache, the code calls `sendRequest`.
{@a send-request}
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="send-request">
</code-example>
@ -799,16 +798,16 @@ It _pipes_ the response through the `tap()` operator,
whose callback adds the response to the cache.
The original response continues untouched back up through the chain of interceptors
to the application caller.
to the application caller.
Data services, such as `PackageSearchService`, are unaware that
Data services, such as `PackageSearchService`, are unaware that
some of their `HttpClient` requests actually return cached responses.
{@a cache-refresh}
#### Return a multi-valued _Observable_
The `HttpClient.get()` method normally returns an _observable_
that either emits the data or an error.
The `HttpClient.get()` method normally returns an _observable_
that either emits the data or an error.
Some folks describe it as a "_one and done_" observable.
But an interceptor can change this to an _observable_ that emits more than once.
@ -817,7 +816,7 @@ A revised version of the `CachingInterceptor` optionally returns an _observable_
immediately emits the cached response, sends the request to the NPM web API anyway,
and emits again later with the updated search results.
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="intercept-refresh">
</code-example>
@ -833,8 +832,8 @@ and adds it to the request before calling `HttpClient.get()`.
</div>
The revised `CachingInterceptor` sets up a server request
whether there's a cached value or not,
The revised `CachingInterceptor` sets up a server request
whether there's a cached value or not,
using the same `sendRequest()` method described [above](#send-request).
The `results$` observable will make the request when subscribed.
@ -849,15 +848,15 @@ Subscribers see a sequence of _two_ responses.
### Listening to progress events
Sometimes applications transfer large amounts of data and those transfers can take a long time.
File uploads are a typical example.
File uploads are a typical example.
Give the users a better experience by providing feedback on the progress of such transfers.
To make a request with progress events enabled, you can create an instance of `HttpRequest`
To make a request with progress events enabled, you can create an instance of `HttpRequest`
with the `reportProgress` option set true to enable tracking of progress events.
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="upload-request"
region="upload-request"
header="app/uploader/uploader.service.ts (upload request)">
</code-example>
@ -873,24 +872,24 @@ When using [`HttpClient#request()`](api/common/http/HttpClient#request) with an
Next, pass this request object to the `HttpClient.request()` method, which
returns an `Observable` of `HttpEvents`, the same events processed by interceptors:
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="upload-body"
header="app/uploader/uploader.service.ts (upload body)" linenums="false">
region="upload-body"
header="app/uploader/uploader.service.ts (upload body)">
</code-example>
The `getEventMessage` method interprets each type of `HttpEvent` in the event stream.
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="getEventMessage"
header="app/uploader/uploader.service.ts (getEventMessage)" linenums="false">
region="getEventMessage"
header="app/uploader/uploader.service.ts (getEventMessage)">
</code-example>
<div class="alert is-helpful">
The sample app for this guide doesn't have a server that accepts uploaded files.
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
intercepts and short-circuits upload requests
by returning an observable of simulated events.
@ -911,45 +910,44 @@ In order to prevent collisions in environments where multiple Angular apps share
<div class="alert is-important">
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
Your backend service must be configured to set the cookie for your page, and to verify that
the header is present on all eligible requests.
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
Your backend service must be configured to set the cookie for your page, and to verify that
the header is present on all eligible requests.
If not, Angular's default protection will be ineffective.
</div>
### Configuring custom cookie/header names
If your backend service uses different names for the XSRF token cookie or header,
If your backend service uses different names for the XSRF token cookie or header,
use `HttpClientXsrfModule.withOptions()` to override the defaults.
<code-example
<code-example
path="http/src/app/app.module.ts"
region="xsrf"
linenums="false">
region="xsrf">
</code-example>
## Testing HTTP requests
Like any external dependency, the HTTP backend needs to be mocked
so your tests can simulate interaction with a remote server.
The `@angular/common/http/testing` library makes
so your tests can simulate interaction with a remote server.
The `@angular/common/http/testing` library makes
setting up such mocking straightforward.
### Mocking philosophy
Angular's HTTP testing library is designed for a pattern of testing wherein
Angular's HTTP testing library is designed for a pattern of testing wherein
the app executes code and makes requests first.
Then a test expects that certain requests have or have not been made,
performs assertions against those requests,
Then a test expects that certain requests have or have not been made,
performs assertions against those requests,
and finally provide responses by "flushing" each expected request.
At the end, tests may verify that the app has made no unexpected requests.
<div class="alert is-helpful">
You can run <live-example stackblitz="specs">these sample tests</live-example>
You can run <live-example stackblitz="specs">these sample tests</live-example>
in a live coding environment.
The tests described in this guide are in `src/testing/http-client.spec.ts`.
@ -960,23 +958,23 @@ There are also tests of an application data service that call `HttpClient` in
### Setup
To begin testing calls to `HttpClient`,
To begin testing calls to `HttpClient`,
import the `HttpClientTestingModule` and the mocking controller, `HttpTestingController`,
along with the other symbols your tests require.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="imports"
header="app/testing/http-client.spec.ts (imports)" linenums="false">
region="imports"
header="app/testing/http-client.spec.ts (imports)">
</code-example>
Then add the `HttpClientTestingModule` to the `TestBed` and continue with
the setup of the _service-under-test_.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="setup"
header="app/testing/http-client.spec.ts(setup)" linenums="false">
region="setup"
header="app/testing/http-client.spec.ts(setup)">
</code-example>
Now requests made in the course of your tests will hit the testing backend instead of the normal backend.
@ -986,47 +984,44 @@ so they can be referenced during the tests.
### Expecting and answering requests
Now you can write a test that expects a GET Request to occur and provides a mock response.
Now you can write a test that expects a GET Request to occur and provides a mock response.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="get-test"
header="app/testing/http-client.spec.ts(httpClient.get)" linenums="false">
region="get-test"
header="app/testing/http-client.spec.ts(httpClient.get)">
</code-example>
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="afterEach"
linenums="false">
region="afterEach">
</code-example>
#### Custom request expectations
If matching by URL isn't sufficient, it's possible to implement your own matching function.
If matching by URL isn't sufficient, it's possible to implement your own matching function.
For example, you could look for an outgoing request that has an authorization header:
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="predicate"
linenums="false">
region="predicate">
</code-example>
As with the previous `expectOne()`,
As with the previous `expectOne()`,
the test will fail if 0 or 2+ requests satisfy this predicate.
#### Handling more than one request
If you need to respond to duplicate requests in your test, use the `match()` API instead of `expectOne()`.
It takes the same arguments but returns an array of matching requests.
Once returned, these requests are removed from future matching and
It takes the same arguments but returns an array of matching requests.
Once returned, these requests are removed from future matching and
you are responsible for flushing and verifying them.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="multi-request"
linenums="false">
region="multi-request">
</code-example>
### Testing for errors
@ -1035,16 +1030,14 @@ You should test the app's defenses against HTTP requests that fail.
Call `request.flush()` with an error message, as seen in the following example.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="404"
linenums="false">
region="404">
</code-example>
Alternatively, you can call `request.error()` with an `ErrorEvent`.
<code-example
path="http/src/testing/http-client.spec.ts"
region="network-error"
linenums="false">
region="network-error">
</code-example>

View File

@ -10,8 +10,8 @@ an AOT-compiled app, translated into French.
{@a angular-i18n}
## Angular and i18n
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
Angular simplifies the following aspects of internationalization:
* Displaying dates, number, percentages, and currencies in a local format.
@ -19,7 +19,7 @@ Angular simplifies the following aspects of internationalization:
* Handling plural forms of words.
* Handling alternative text.
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
After you have set up your app to use i18n, the CLI can help you with the following steps:
* Extracting localizable text into a file that you can send out to be translated.
* Building and serving the app for a given locale, using the translated text.
@ -85,8 +85,7 @@ The CLI imports the locale data for you when you use the parameter `--configurat
If you want to import locale data for other languages, you can do it manually:
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts"></code-example>
The first parameter is an object containing the locale data imported from `@angular/common/locales`.
By default, the imported locale data is registered with the locale id that is defined in the Angular
@ -100,8 +99,7 @@ The files in `@angular/common/locales` contain most of the locale data that you
need, but some advanced formatting options might only be available in the extra dataset that you can
import from `@angular/common/locales/extra`. An error message informs you when this is the case.
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts"></code-example>
<div class="alert is-helpful">
@ -132,9 +130,9 @@ The i18n template translation process has four phases:
* `--i18nFile`=*path to the translation file*
* `--i18nFormat`=*format of the translation file*
* `--i18nLocale`= *locale id*
* `--i18nLocale`= *locale id*
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
You need to build and deploy a separate version of the app for each supported language.
@ -146,13 +144,11 @@ text is to be translated.
In the example below, an `<h1>` tag displays a simple English language greeting, "Hello i18n!"
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html"></code-example>
To mark the greeting for translation, add the `i18n` attribute to the `<h1>` tag.
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -171,8 +167,7 @@ To translate a text message accurately, the translator may need additional infor
You can add a description of the text message as the value of the `i18n` attribute, as shown in the
example below:
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html"></code-example>
The translator may also need to know the meaning or intent of the text message within this particular
app context.
@ -180,8 +175,7 @@ app context.
You add context by beginning the `i18n` attribute value with the _meaning_ and
separating it from the _description_ with the `|` character: `<meaning>|<description>`
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html"></code-example>
All occurrences of a text message that have the same meaning will have the same translation.
A text message that is associated with different meanings can have different translations.
@ -199,8 +193,7 @@ text messages with different descriptions (not different meanings), then they ar
The angular i18n extractor tool generates a file with a translation unit entry for each `i18n`
attribute in a template. By default, it assigns each translation unit a unique id such as this one:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id"></code-example>
When you change the translatable text, the extractor tool generates a new id for that translation unit.
You must then update the translation file with the new id.
@ -208,14 +201,12 @@ You must then update the translation file with the new id.
Alternatively, you can specify a custom id in the `i18n` attribute by using the prefix `@@`.
The example below defines the custom id `introductionHeader`:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html' linenums="false">
</code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html'></code-example>
When you specify a custom id, the extractor tool and compiler generate a translation unit with that
custom id.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id"></code-example>
The custom id is persistent. The extractor tool does not change it when the translatable text changes.
Therefore, you do not need to update the translation. This approach makes maintenance easier.
@ -226,13 +217,11 @@ You can use a custom id in combination with a description by including both in t
`i18n` attribute. In the example below, the `i18n` attribute value includes a description, followed
by the custom `id`:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html' linenums="false">
</code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html'></code-example>
You also can add a meaning, as shown in this example:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html' linenums="false">
</code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html'></code-example>
#### Define unique custom ids
@ -275,8 +264,7 @@ However, if you don't want to create a new DOM element merely to facilitate tran
you can wrap the text in an `<ng-container>` element.
The `<ng-container>` is transformed into an html comment:
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html"></code-example>
{@a translate-attributes}
### Translate attributes
@ -284,15 +272,13 @@ The `<ng-container>` is transformed into an html comment:
Displayed text is sometimes supplied as the value of an attribute, rather than the content of tag.
For example, if your template has an image with a `title` attribute, the text value of the `title` attribute needs to be translated.
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html"></code-example>
To mark an attribute for translation, add an attribute in the form of `i18n-x`,
where `x` is the name of the attribute to translate. The following example shows how to mark the
`title` attribute for translation by adding the `i18n-title` attribute on the `img` tag:
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html"></code-example>
This technique works for any attribute of any element.
@ -301,8 +287,8 @@ syntax.
## Regular expressions for plurals and selections
Different languages have different pluralization rules and grammatical constructions that add
complexity to the translation task.
Different languages have different pluralization rules and grammatical constructions that add
complexity to the translation task.
You can use regular expressions with the `plural` and `select` clauses to provide patterns that aid translation in these cases.
{@a plural-ICU}
@ -316,8 +302,7 @@ Other languages might express the cardinality differently.
The example below shows how to use a `plural` ICU expression to display one of those three options
based on when the update occurred:
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html"></code-example>
* The first parameter is the key. It is bound to the component property (`minutes`), which determines
the number of minutes.
@ -372,8 +357,7 @@ The following format message in the component template binds to the component's
which outputs one of the following string values: "male", "female" or "other".
The message maps those values to the appropriate translations:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
{@a nesting-ICUS}
### Nesting plural and select ICU expressions
@ -395,7 +379,7 @@ Open a terminal window at the root of the app project and run the CLI command `x
ng xi18n
</code-example>
By default, the command creates a file named `messages.xlf` in your `src/` folder.
By default, the command creates a file named `messages.xlf` in your `src/` folder.
<div class="alert is-helpful">
@ -411,7 +395,7 @@ For more information, see the [Angular Ahead-of-Time Webpack Plugin documentatio
{@a other-formats}
### Output options
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
For example, to create a file in the `src/locale` folder, specify the output path:
<code-example language="sh" class="code-shell">
@ -472,7 +456,7 @@ file. This information is not used by Angular, but external translation tools ma
The `ng xi18n` command generates a translation source file named `messages.xlf` in the project `src`
folder.
The next step is to translate the display strings in this source file into language-specific
The next step is to translate the display strings in this source file into language-specific
translation files. The example in this guide creates a French translation file.
{@a localization-folder}
@ -518,8 +502,7 @@ This sample file is easy to translate without a special editor or knowledge of F
1. Open `messages.fr.xlf` and find the first `<trans-unit>` section:
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
> This XML element represents the translation of the `<h1>` greeting tag that you marked with the
`i18n` attribute earlier in this guide.
@ -534,13 +517,11 @@ This sample file is easy to translate without a special editor or knowledge of F
and context provided by the source, description, and meaning elements to guide your selection of
the appropriate French translation.
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;, after translation)" linenums="false">
</code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;, after translation)"></code-example>
3. Translate the other text nodes the same way:
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<div class="alert is-important">
@ -566,8 +547,7 @@ must be just below the translation unit for the logo.
To translate a `plural`, translate its ICU format match values:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
You can add or remove plural cases, with each language having its own cardinality. (See
[CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).)
@ -577,8 +557,7 @@ You can add or remove plural cases, with each language having its own cardinalit
Below is the content of our example `select` ICU expression in the component template:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
The extraction tool broke that into two translation units because ICU expressions are extracted
separately.
@ -588,19 +567,16 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and move around the placeholder if necessary, but don't remove it. If you remove
the placeholder, the ICU expression will not be present in your translated app.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
The second translation unit, immediately below the first one, contains the `select` message.
Translate that as well.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
Here they are together, after translation:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
{@a translate-nested}
### Translate a nested expression
@ -608,18 +584,15 @@ Here they are together, after translation:
A nested expression is similar to the previous examples. As in the previous example, there are
two translation units. The first one contains the text outside of the nested expression:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
The second unit contains the complete nested expression:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
And both together:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
The entire template translation is complete. The next section describes how to load that translation
into the app.
@ -783,8 +756,7 @@ behavior of the compiler. You can use it to specify the translation providers:
Then provide the `LOCALE_ID` in the main module:
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts" linenums="false">
</code-example>
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts"></code-example>
{@a missing-translation}

View File

@ -88,9 +88,7 @@ placeholder markup in `app.component.html` with a custom nav
so you can easily navigate to your modules in the browser:
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
@ -128,9 +126,7 @@ Each feature module acts as a doorway via the router. In the `AppRoutingModule`,
In `AppRoutingModule`, update the `routes` array with the following:
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts" linenums="false">
</code-example>
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></code-example>
The import statements stay the same. The first two paths are the routes to the `CustomersModule` and the `OrdersModule` respectively. Notice that the lazy loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports. The import path is the relative path to the module.
@ -140,9 +136,7 @@ The import statements stay the same. The first two paths are the routes to the `
Next, take a look at `customers.module.ts`. If youre using the CLI and following the steps outlined in this page, you dont have to do anything here. The feature module is like a connector between the `AppRoutingModule` and the feature routing module. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts" linenums="false">
</code-example>
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
@ -153,18 +147,14 @@ The `customers.module.ts` file imports the `CustomersRoutingModule` and `Custome
The next step is in `customers-routing.module.ts`. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to `CustomerListComponent`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts" linenums="false">
</code-example>
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts"></code-example>
Notice that the `path` is set to an empty string. This is because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. Every route in this routing module is a child route.
Repeat this last step of importing the `OrdersListComponent` and configuring the Routes array for the `orders-routing.module.ts`:
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)" linenums="false">
</code-example>
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
Now, if you view the app in the browser, the three buttons take you to each module.

View File

@ -23,7 +23,7 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component:
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></code-example>
No directive or component will implement all of the lifecycle hooks.
Angular only calls a directive/component hook method *if it is defined*.
@ -339,13 +339,13 @@ The heroes will never know they're being watched.
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
that log messages to the parent via an injected `LoggerService`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></code-example>
You can apply the spy to any native or component element and it'll be initialized and destroyed
at the same time as that element.
Here it is attached to the repeated hero `<div>`:
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></code-example>
Each spy's birth and death marks the birth and death of the attached hero `<div>`
with an entry in the *Hook Log* as seen here:
@ -425,7 +425,7 @@ You risk memory leaks if you neglect to do so.
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></code-example>
The `ngOnChanges()` method takes an object that maps each changed property name to a
[SimpleChange](api/core/SimpleChange) object holding the current and previous property values.
@ -433,7 +433,7 @@ This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></code-example>
The host `OnChangesParentComponent` binds to them like this:
@ -468,7 +468,7 @@ Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></code-example>
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
@ -497,25 +497,25 @@ The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` h
Here's a child view that displays a hero's name in an `<input>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent"></code-example>
The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)"></code-example>
The following hooks take action based on changing values *within the child view*,
which can only be reached by querying for the child view via the property decorated with
[@ViewChild](api/core/ViewChild).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></code-example>
{@a wait-a-tick}
### Abide by the unidirectional data flow rule
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></code-example>
Why does the `doSomething()` method wait a tick before updating `comment`?
@ -559,7 +559,7 @@ Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afte
This time, instead of including the child view within the template, it imports the content from
the `AfterContentComponent`'s parent. Here's the parent's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></code-example>
Notice that the `<app-child>` tag is tucked between the `<after-content>` tags.
Never put content between a component's element tags *unless you intend to project that content
@ -567,7 +567,7 @@ into the component*.
Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></code-example>
The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
@ -603,7 +603,7 @@ The following *AfterContent* hooks take action based on changing values in a *co
which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/ContentChild).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)" linenums="false"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)"></code-example>
{@a no-unidirectional-flow-worries}

View File

@ -470,8 +470,7 @@ You can throw an error or take other remedial action.
Certain NgModules, such as `BrowserModule`, implement such a guard.
Here is a custom constructor for an NgModule called `GreetingModule`.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)" linenums="false">
</code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)"></code-example>
<hr/>

View File

@ -51,7 +51,8 @@ You then import these modules into the root module.
The [Angular CLI](cli) generates the following basic `AppModule` when creating a new app.
<code-example path="ngmodules/src/app/app.module.1.ts" header="src/app/app.module.ts (default AppModule)" linenums="false"> // @NgModule decorator with its metadata
<code-example path="ngmodules/src/app/app.module.1.ts" header="src/app/app.module.ts (default AppModule)">
// @NgModule decorator with its metadata
</code-example>
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). For more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).

View File

@ -26,18 +26,14 @@ In this page, you'll use pipes to transform a component's birthday property into
a human-friendly date.
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts"></code-example>
Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html"></code-example>
@ -79,9 +75,7 @@ Modify the birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html"></code-example>
@ -95,9 +89,7 @@ Write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component:
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)" linenums="false">
</code-example>
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)"></code-example>
@ -106,9 +98,7 @@ That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`).
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)" linenums="false">
</code-example>
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)"></code-example>
@ -143,9 +133,7 @@ the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
The birthday displays as **<samp>APR 15, 1988</samp>**.
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html"></code-example>
@ -153,9 +141,7 @@ This example&mdash;which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**&mdash;
the same pipes as above, but passes in a parameter to `date` as well.
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html"></code-example>
@ -166,9 +152,7 @@ You can write your own custom pipes.
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts"></code-example>
@ -200,8 +184,7 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
<figure>
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
@ -271,17 +254,13 @@ In the next example, the component uses the default, aggressive change detection
its display of every hero in the `heroes` array. Here's the template:
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)"></code-example>
The companion component class provides heroes, adds heroes into the array, and can reset the array.
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)"></code-example>
@ -293,17 +272,13 @@ If you added the ability to remove or change a hero, Angular would detect those
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)"></code-example>
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts"></code-example>
@ -315,9 +290,7 @@ It's just using a different change-detection algorithm that ignores changes to t
Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
@ -365,9 +338,7 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts" linenums="false">
</code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
@ -441,18 +412,14 @@ The only difference is the `pure` flag in the pipe metadata.
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" header="src/app/flying-heroes.pipe.ts (filter)" region="filter">
</code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
<code-example path="pipes/src/app/flying-heroes-impure.component.html" linenums="false" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes">
</code-example>
<code-example path="pipes/src/app/flying-heroes-impure.component.html" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>

View File

@ -15,7 +15,7 @@ ng generate service User
This command creates the following `UserService` skeleton:
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
You can now inject `UserService` anywhere in your application.
@ -32,11 +32,11 @@ You should always provide your service in the root injector unless there is a ca
It's also possible to specify that a service should be provided in a particular `@NgModule`. For example, if you don't want `UserService` to be available to applications unless they import a `UserModule` you've created, you can specify that the service should be provided in the module:
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts"></code-example>
The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module:
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts" linenums="false"> </code-example>
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts"></code-example>
## Limiting provider scope by lazy loading modules
@ -61,8 +61,7 @@ method is helpful for when you want to eagerly load a module that needs a servic
Providing a service in the component limits the service only to that component (other components in
the same module cant access it.)
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
## Providing services in modules vs. components

View File

@ -12,7 +12,7 @@ Try the <live-example title="Reactive Forms in Stackblitz">Reactive Forms live-e
## Introduction to reactive forms
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
Reactive forms also provide a straightforward path to testing because you are assured that your data is consistent and predictable when requested. Any consumers of the streams have access to manipulate that data safely.
@ -26,11 +26,9 @@ This section describes how to add a single form control. In the example, the use
To use reactive forms, import `ReactiveFormsModule` from the `@angular/forms` package and add it to your NgModule's `imports` array.
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)">
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)"></code-example>
</code-example>
### Step 2: Generating and importing a new form control
### Step 2: Generating and importing a new form control
Generate a component for the control.
@ -42,19 +40,15 @@ Generate a component for the control.
The `FormControl` class is the basic building block when using reactive forms. To register a single form control, import the `FormControl` class into your component and create a new instance of the form control to save as a class property.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts">
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts"></code-example>
</code-example>
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
### Step 3: Registering the control in the template
After you create the control in the component class, you must associate it with a form control element in the template. Update the template with the form control using the `formControl` binding provided by `FormControlDirective` included in `ReactiveFormsModule`.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" linenums="false" header="src/app/name-editor/name-editor.component.html">
</code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" header="src/app/name-editor/name-editor.component.html"></code-example>
<div class="alert is-helpful">
@ -66,11 +60,9 @@ Using the template binding syntax, the form control is now registered to the `na
#### Displaying the component
The form control assigned to `name` is displayed when the component is added to a template.
The form control assigned to `name` is displayed when the component is added to a template.
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" linenums="false" header="src/app/app.component.html (name editor)">
</code-example>
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example>
<figure>
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
@ -78,23 +70,21 @@ The form control assigned to `name` is displayed when the component is added to
## Managing control values
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
the current state and value through the component class or the component template. The following examples display the value of the form control instance and change it.
{@a display-value}
### Displaying a form control value
You can display the value in these ways:
You can display the value in these ways:
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
* With the `value` property. which gives you a snapshot of the current value.
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
* With the `value` property. which gives you a snapshot of the current value.
The following example shows you how to display the current value using interpolation in the template.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" linenums="false" header="src/app/name-editor/name-editor.component.html (control value)">
</code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" header="src/app/name-editor/name-editor.component.html (control value)"></code-example>
The displayed value changes as you update the form control element.
@ -104,7 +94,7 @@ Read about other `FormControl` properties and methods in the [Reactive forms API
### Replacing a form control value
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
The following example adds a method to the component class to update the value of the control to *Nancy* using the `setValue()` method.
@ -112,11 +102,9 @@ The following example adds a method to the component class to update the value o
</code-example>
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" linenums="false" header="src/app/name-editor/name-editor.component.html (update value)">
</code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" header="src/app/name-editor/name-editor.component.html (update value)"></code-example>
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
@ -162,21 +150,17 @@ The individual form controls are now collected within a group. A `FormGroup` ins
A form group tracks the status and changes for each of its controls, so if one of the controls changes, the parent control also emits a new status or value change. The model for the group is maintained from its members. After you define the model, you must update the template to reflect the model in the view.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template form group)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
### Saving form data
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
Add an `ngSubmit` event listener to the `form` tag with the `onSubmit()` callback method.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit event)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" header="src/app/profile-editor/profile-editor.component.html (submit event)"></code-example>
The `onSubmit()` method in the `ProfileEditor` component captures the current value of `profileForm`. Use `EventEmitter` to keep the form encapsulated and to provide the form value outside the component. The following example uses `console.warn` to log a message to the browser console.
@ -184,13 +168,11 @@ The `onSubmit()` method in the `ProfileEditor` component captures the current va
</code-example>
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
Use a `button` element to add a button to the bottom of the form to trigger the form submission.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit button)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" header="src/app/profile-editor/profile-editor.component.html (submit button)"></code-example>
<div class="alert is-helpful">
@ -202,9 +184,7 @@ Use a `button` element to add a button to the bottom of the form to trigger the
To display the `ProfileEditor` component that contains the form, add it to a component template.
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" linenums="false" header="src/app/app.component.html (profile editor)">
</code-example>
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" header="src/app/app.component.html (profile editor)"></code-example>
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
@ -220,9 +200,7 @@ When building complex forms, managing the different areas of information is easi
An address is a good example of information that can be grouped together. Form groups can accept both form control and form group instances as children. This makes composing complex form models easier to maintain and logically group together. To create a nested group in `profileForm`, add a nested `address` element to the form group instance.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.ts (nested form group)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" header="src/app/profile-editor/profile-editor.component.ts (nested form group)"></code-example>
In this example, `address group` combines the current `firstName` and `lastName` controls with the new `street`, `city`, `state`, and `zip` controls. Even though the `address` element in the form group is a child of the overall `profileForm` element in the form group, the same rules apply with value and status changes. Changes in status and value from the nested form group propagate to the parent form group, maintaining consistency with the overall model.
@ -232,9 +210,7 @@ After you update the model in the component class, update the template to connec
Add the `address` form group containing the `street`, `city`, `state`, and `zip` fields to the `ProfileEditor` template.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template nested form group)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" header="src/app/profile-editor/profile-editor.component.html (template nested form group)"></code-example>
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
@ -254,11 +230,11 @@ When updating the value for a form group instance that contains multiple control
### Patching the model value
There are two ways to update the model value:
There are two ways to update the model value:
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
The strict checks of the `setValue()` method help catch nesting errors in complex forms, while `patchValue()` fails silently on those errors.
@ -270,15 +246,13 @@ In `ProfileEditorComponent`, use the `updateProfile` method with the example bel
Simulate an update by adding a button to the template to update the user profile on demand.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" linenums="false" header="src/app/profile-editor/profile-editor.component.html (update value)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" header="src/app/profile-editor/profile-editor.component.html (update value)"></code-example>
When a user clicks the button, the `profileForm` model is updated with new values for `firstName` and `street`. Notice that `street` is provided in an object inside the `address` property. This is necessary because the `patchValue()` method applies the update against the model structure. `PatchValue()` only updates properties that the form model defines.
## Generating form controls with FormBuilder
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
The following section refactors the `ProfileEditor` component to use the form builder service to create form control and form group instances.
@ -300,7 +274,7 @@ The `FormBuilder` service is an injectable provider that is provided with the re
### Step 3: Generating form controls
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
Use the `group` method to create the `profileForm` controls.
@ -321,7 +295,7 @@ Compare using the form builder to creating the instances manually.
<code-tabs>
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (instances)">
</code-pane>
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (form builder)">
@ -356,9 +330,7 @@ In the `ProfileEditor` component, add the `Validators.required` static method as
HTML5 has a set of built-in attributes that you can use for native validation, including `required`, `minlength`, and `maxlength`. You can take advantage of these optional attributes on your form input elements. Add the `required` attribute to the `firstName` input element.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" linenums="false" header="src/app/profile-editor/profile-editor.component.html (required attribute)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" header="src/app/profile-editor/profile-editor.component.html (required attribute)"></code-example>
<div class="alert is-important">
@ -372,9 +344,7 @@ When you add a required field to the form control, its initial status is invalid
Display the current status of `profileForm` using interpolation.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" linenums="false" header="src/app/profile-editor/profile-editor.component.html (display status)">
</code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
@ -410,7 +380,7 @@ The aliases control in the form group instance is now populated with a single co
### Step 3: Accessing the FormArray control
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
@ -434,15 +404,13 @@ In the template, each control is displayed as a separate input field.
### Step 4: Displaying the form array in the template
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
Add the template HTML below after the `<div>` closing the `formGroupName` element.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)">
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)"></code-example>
</code-example>
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
@ -554,7 +522,7 @@ Listed below are the base classes and services used to create and manage form co
</td>
</tr>
</tr>
</table>

View File

@ -15,8 +15,7 @@ The [AnimationOptions](https://angular.io/api/animations/AnimationOptions) inter
To create a reusable animation, use the [`animation()`](https://angular.io/api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your app components using the [`useAnimation()`](https://angular.io/api/animations/useAnimation) API.
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
In the above code snippet, `transAnimation` is made reusable by declaring it as an export variable.
@ -27,8 +26,7 @@ In the above code snippet, `transAnimation` is made reusable by declaring it as
You can import the reusable `transAnimation` variable in your component class and reuse it using the `useAnimation()` method as shown below.
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript"></code-example>
## More on Angular animations
@ -37,4 +35,4 @@ You may also be interested in the following:
* [Introduction to Angular animations](guide/animations)
* [Transition and triggers](guide/transition-and-triggers)
* [Complex animation Sequences](guide/complex-animation-sequences)
* [Route transition animations](guide/route-animations)
* [Route transition animations](guide/route-animations)

View File

@ -43,8 +43,7 @@ Use the `RouterModule.forRoot` method to define a set of routes. Also, import th
The following configuration defines the possible routes for the application.
<code-example path="animations/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts" region="route-animation-data" language="typescript">
</code-example>
<code-example path="animations/src/app/app.module.ts" header="src/app/app.module.ts" region="route-animation-data" language="typescript"></code-example>
The `home` and `about` paths are associated with the `HomeComponent` and `AboutComponent` views. The route configuration tells the Angular router to instantiate the `HomeComponent` and `AboutComponent` views when the navigation matches the corresponding path.
@ -62,13 +61,11 @@ After configuring the routes, tell the Angular router where to render the views
The `<router-outlet>` container has an attribute directive that contains data about active routes and their states, based on the `data` property that we set in the route configuration.
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet">
</code-example>
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet"></code-example>
`AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens.
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript">
</code-example>
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript"></code-example>
Here, the `prepareRoute()` method takes the value of the output directive (established through `#outlet="outlet"`) and returns a string value representing the state of the animation based on the custom data of the current active route. You can use this data to control which transition to execute for each route.
@ -79,8 +76,7 @@ Animations can be defined directly inside your components. For this example we a
The following code snippet defines a reusable animation named `slideInAnimation`.
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="route-animations" language="typescript">
</code-example>
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="route-animations" language="typescript"></code-example>
The animation definition does several things:
@ -97,15 +93,13 @@ A route change activates the animation trigger, and a transition matching the st
Make the animation definition available in your application by adding the reusable animation (`slideInAnimation`) to the `animations` metadata of the `AppComponent`.
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="define" language="typescript">
</code-example>
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="define" language="typescript"></code-example>
### Styling the host and child components
During a transition, a new view is inserted directly after the old one and both elements appear on screen at the same time. To prevent this, apply additional styling to the host view, and to the removed and inserted child views. The host view must use relative positioning, and the child views must use absolute positioning. Adding styling to the views animates the containers in place, without the DOM moving things around.
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="style-view" language="typescript">
</code-example>
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="style-view" language="typescript"></code-example>
### Querying the view containers
@ -113,8 +107,7 @@ Use the `query()` method to find and animate elements within the current host co
Let's assume that we are routing from the *Home => About*.
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="query" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="query" language="typescript"></code-example>
The animation code does the following after styling the views:

View File

@ -53,9 +53,7 @@ If the `app` folder is the application root, as it is for the sample application
set the `href` value *exactly* as shown here.
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
@ -69,9 +67,7 @@ It is not part of the Angular core. It is in its own library package, `@angular/
Import what you need from it as you would from any other Angular package.
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (import)" region="import-router">
</code-example>
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (import)" region="import-router"></code-example>
@ -100,9 +96,7 @@ The following example creates five route definitions, configures the router via
and adds the result to the `AppModule`'s `imports` array.
<code-example path="router/src/app/app.module.0.ts" linenums="false" header="src/app/app.module.ts (excerpt)">
</code-example>
<code-example path="router/src/app/app.module.0.ts" header="src/app/app.module.ts (excerpt)"></code-example>
@ -177,9 +171,7 @@ an anchor tag.
Consider the following template:
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
</code-example>
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
The `RouterLink` directives on the anchor tags give the router control over those elements.
The navigation paths are fixed, so you can assign a string to the `routerLink` (a "one-time" binding).
@ -917,9 +909,7 @@ In order to use the Router, you must first register the `RouterModule` from the
</div>
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (first-config)" region="first-config">
</code-example>
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (first-config)" region="first-config"></code-example>
<div class="alert is-helpful">
@ -947,9 +937,7 @@ The router outlet serves as a placeholder when the routed components will be ren
The corresponding component template looks like this:
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
</code-example>
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
{@a wildcard}
@ -973,17 +961,13 @@ Be sure it is the _last_ route in the configuration.
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to `"/sidekicks"`.
<code-example path="router/src/app/hero-list/hero-list.component.1.html" linenums="false" header="src/app/hero-list/hero-list.component.html (excerpt)">
</code-example>
<code-example path="router/src/app/hero-list/hero-list.component.1.html" header="src/app/hero-list/hero-list.component.html (excerpt)"></code-example>
The application will fail if the user clicks that button because you haven't defined a `"/sidekicks"` route yet.
Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead and have it navigate to a simple `PageNotFoundComponent`.
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (wildcard)" region="wildcard">
</code-example>
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (wildcard)" region="wildcard"></code-example>
Create the `PageNotFoundComponent` to display when users visit invalid URLs.
@ -991,9 +975,7 @@ Create the `PageNotFoundComponent` to display when users visit invalid URLs.
ng generate component page-not-found
</code-example>
<code-example path="router/src/app/page-not-found/page-not-found.component.html" linenums="false" header="src/app/page-not-found.component.html (404 component)">
</code-example>
<code-example path="router/src/app/page-not-found/page-not-found.component.html" header="src/app/page-not-found.component.html (404 component)"></code-example>
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.
@ -1022,8 +1004,7 @@ Add the default route somewhere _above_ the wildcard route.
It's just above the wildcard route in the following excerpt showing the complete `appRoutes` for this milestone.
<code-example path="router/src/app/app-routing.module.1.ts" linenums="false" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes">
</code-example>
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes"></code-example>
A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route.
@ -1314,16 +1295,12 @@ By re-exporting the `RouterModule` here the components declared in `AppModule` w
After these steps, the file should look like this.
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts">
</code-example>
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts"></code-example>
Next, update the `app.module.ts` file, removing `RouterModule.forRoot` in
the `imports` array.
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts">
</code-example>
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts"></code-example>
@ -1442,9 +1419,7 @@ Next, you'll update the `HeroesModule` metadata.
* Import and add the `HeroDetailComponent` and `HeroListComponent` to the `declarations` array in the `HeroesModule`.
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts">
</code-example>
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts"></code-example>
@ -1568,9 +1543,7 @@ In any other module, you must call the **`RouterModule.forChild`** method to reg
The updated `HeroesRoutingModule` looks like this:
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts">
</code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts"></code-example>
@ -1602,9 +1575,7 @@ Remove the `HeroListComponent` import and the `/heroes` route from the `app-rout
These are concerns at the top level of the application itself.
<code-example path="router/src/app/app-routing.module.2.ts" linenums="false" header="src/app/app-routing.module.ts (v2)">
</code-example>
<code-example path="router/src/app/app-routing.module.2.ts" header="src/app/app-routing.module.ts (v2)"></code-example>
@ -1618,9 +1589,7 @@ Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it'
After these steps, the `AppModule` should look like this:
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts">
</code-example>
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts"></code-example>
@ -1632,9 +1601,7 @@ After these steps, the `AppModule` should look like this:
Look at the module `imports` array. Notice that the `AppRoutingModule` is _last_.
Most importantly, it comes _after_ the `HeroesModule`.
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)" linenums="false">
</code-example>
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)"></code-example>
@ -1678,9 +1645,7 @@ Return to the `HeroesRoutingModule` and look at the route definitions again.
The route to `HeroDetailComponent` has a twist.
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route">
</code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route"></code-example>
@ -1734,9 +1699,7 @@ Accordingly, the _link parameters array_ has *two* items: the routing _path_ an
`id` of the selected hero.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
@ -1765,18 +1728,14 @@ the `HeroDetailComponent` via the `ActivatedRoute` service.
Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports"></code-example>
Import the `switchMap` operator because you need it later to process the `Observable` route parameters.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import"></code-example>
@ -1787,17 +1746,13 @@ As usual, you write a constructor that asks Angular to inject services
that the component requires and reference them as private variables.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor"></code-example>
Later, in the `ngOnInit` method, you use the `ActivatedRoute` service to retrieve the parameters for the route,
pull the hero `id` from the parameters and retrieve the hero to display.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit"></code-example>
The `paramMap` processing is a bit tricky. When the map changes, you `get()`
the `id` parameter from the changed parameters.
@ -1934,9 +1889,7 @@ You can access the parameters directly without subscribing or adding observable
It's much simpler to write and read:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot"></code-example>
@ -1967,9 +1920,7 @@ that you can bind to a `[routerLink]` directive.
It holds the _path to the `HeroListComponent`_:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes"></code-example>
{@a optional-route-parameters}
@ -2029,9 +1980,7 @@ When navigating to the `HeroDetailComponent` you specified the _required_ `id` o
*route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array).
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
@ -2039,9 +1988,7 @@ The router embedded the `id` value in the navigation URL because you had defined
as a route parameter with an `:id` placeholder token in the route `path`:
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route">
</code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route"></code-example>
@ -2049,9 +1996,7 @@ When the user clicks the back button, the `HeroDetailComponent` constructs anoth
which it uses to navigate back to the `HeroListComponent`.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes"></code-example>
@ -2066,9 +2011,7 @@ For demonstration purposes, there's an extra junk parameter (`foo`) in the objec
Here's the revised navigation statement:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes"></code-example>
@ -2148,27 +2091,21 @@ This time you'll be navigating in the opposite direction, from the `HeroDetailCo
First you extend the router import statement to include the `ActivatedRoute` service symbol:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router"></code-example>
Import the `switchMap` operator to perform an operation on the `Observable` of route parameter map.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports"></code-example>
Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor"></code-example>
@ -2181,15 +2118,11 @@ The binding adds the `selected` CSS class when the comparison returns `true` and
Look for it within the repeated `<li>` tag as shown here:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" header="src/app/heroes/hero-list/hero-list.component.html"></code-example>
Add some styles to apply when the list item is selected.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" linenums="false" region="selected" header="src/app/heroes/hero-list/hero-list.component.css">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" region="selected" header="src/app/heroes/hero-list/hero-list.component.css"></code-example>
@ -2215,22 +2148,16 @@ This section shows you how to add some [animations](guide/animations) to the `He
First import the `BrowserAnimationsModule` and add it to the `imports` array:
<code-example path="router/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts (animations-module)" region="animations-module">
</code-example>
<code-example path="router/src/app/app.module.ts" header="src/app/app.module.ts (animations-module)" region="animations-module"></code-example>
Next, add a `data` object to the routes for `HeroListComponent` and `HeroDetailComponent`. Transitions are based on `states` and you'll use the `animation` data from the route to provide a named animation `state` for the transitions.
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)">
</code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)"></code-example>
Create an `animations.ts` file in the root `src/app/` folder. The contents look like this:
<code-example path="router/src/app/animations.ts" linenums="false" header="src/app/animations.ts (excerpt)">
</code-example>
<code-example path="router/src/app/animations.ts" header="src/app/animations.ts (excerpt)"></code-example>
This file does the following:
@ -2248,24 +2175,18 @@ Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/r
Add an `animations` array to the `@Component` metadata's that contains the `slideInAnimation`.
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (animations)" region="animation-imports">
</code-example>
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (animations)" region="animation-imports"></code-example>
In order to use the routable animations, you'll need to wrap the `RouterOutlet` inside an element. You'll
use the `@routeAnimation` trigger and bind it to the element.
For the `@routeAnimation` transitions to key off states, you'll need to provide it with the `data` from the `ActivatedRoute`. The `RouterOutlet` is exposed as an `outlet` template variable, so you bind a reference to the router outlet. A variable of `routerOutlet` is an ideal choice.
<code-example path="router/src/app/app.component.2.html" linenums="false" header="src/app/app.component.html (router outlet)">
</code-example>
<code-example path="router/src/app/app.component.2.html" header="src/app/app.component.html (router outlet)"></code-example>
The `@routeAnimation` property is bound to the `getAnimationData` with the provided `routerOutlet` reference, so you'll need to define that function in the `AppComponent`. The `getAnimationData` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (router outlet)" region="function-binding">
</code-example>
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (router outlet)" region="function-binding"></code-example>
When switching between the two routes, the `HeroDetailComponent` and `HeroListComponent` will ease in from the left when routed to and will slide to the right when navigating away.
@ -2563,9 +2484,7 @@ Begin by imitating the heroes feature:
You'll use mock crises instead of mock heroes:
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts">
</code-example>
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts"></code-example>
The resulting crisis center is a foundation for introducing a new concept&mdash;**child routing**.
@ -2622,8 +2541,7 @@ Generate a `CrisisCenter` component in the `crisis-center` folder:
Update the component template to look like this:
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" linenums="false" header="src/app/crisis-center/crisis-center/crisis-center.component.html">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" header="src/app/crisis-center/crisis-center/crisis-center.component.html"></code-example>
The `CrisisCenterComponent` has the following in common with the `AppComponent`:
@ -2650,14 +2568,12 @@ As a host page for the "Crisis Center" feature, generate a `CrisisCenterHome` co
Update the template with a welcome message to the `Crisis Center`.
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" linenums="false" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html"></code-example>
Update the `crisis-center-routing.module.ts` you renamed after copying it from `heroes-routing.module.ts` file.
This time, you define **child routes** *within* the parent `crisis-center` route.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes"></code-example>
Notice that the parent `crisis-center` route has a `children` property
@ -2703,9 +2619,7 @@ The absolute URL for the latter example, including the `localhost` origin, is
Here's the complete `crisis-center-routing.module.ts` file with its imports.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)"></code-example>
@ -2723,7 +2637,7 @@ _before_ the `AppRoutingModule`:
</code-pane>
<code-pane path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
<code-pane path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
</code-pane>
@ -2735,9 +2649,7 @@ The feature routes are now provided by the `HeroesModule` and the `CrisisCenter`
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (v3)" region="v3">
</code-example>
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (v3)" region="v3"></code-example>
@ -2814,9 +2726,7 @@ 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.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate"></code-example>
Notice that the path goes up a level using the `../` syntax.
@ -2847,9 +2757,7 @@ Multiple outlets can be displaying different content, determined by different ro
Add an outlet named "popup" in the `AppComponent`, directly below the unnamed outlet.
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (outlets)" region="outlets">
</code-example>
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (outlets)" region="outlets"></code-example>
@ -2923,9 +2831,7 @@ That's a peculiarity covered [below](#clear-secondary-routes).
Open the `AppRoutingModule` and add a new `compose` route to the `appRoutes`.
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (compose route)" region="compose">
</code-example>
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (compose route)" region="compose"></code-example>
@ -2936,9 +2842,7 @@ This route now targets the popup outlet and the `ComposeMessageComponent` will d
The user needs a way to open the popup.
Open the `AppComponent` and add a "Contact" link.
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (contact-link)" region="contact-link">
</code-example>
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (contact-link)" region="contact-link"></code-example>
@ -3031,9 +2935,7 @@ 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 `closePopup()` method again:
<code-example path="router/src/app/compose-message/compose-message.component.ts" linenums="false" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup">
</code-example>
<code-example path="router/src/app/compose-message/compose-message.component.ts" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup"></code-example>
@ -3266,11 +3168,11 @@ feature module, a dashboard route and two unfinished components to manage crises
<code-tabs>
<code-pane header="src/app/admin/admin/admin.component.html" linenums="false" path="router/src/app/admin/admin/admin.component.html">
<code-pane header="src/app/admin/admin/admin.component.html" path="router/src/app/admin/admin/admin.component.html">
</code-pane>
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" linenums="false" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
</code-pane>
@ -3278,11 +3180,11 @@ feature module, a dashboard route and two unfinished components to manage crises
</code-pane>
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" linenums="false" path="router/src/app/admin/manage-crises/manage-crises.component.html">
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" path="router/src/app/admin/manage-crises/manage-crises.component.html">
</code-pane>
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" linenums="false" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
</code-pane>
@ -3309,9 +3211,7 @@ is considered a match to any route within the admin feature area. You only want
The initial admin routing configuration:
<code-example path="router/src/app/admin/admin-routing.module.1.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes">
</code-example>
<code-example path="router/src/app/admin/admin-routing.module.1.ts" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes"></code-example>
Looking at the child route under the `AdminComponent`, there is a `path` and a `children`
property but it's not using a `component`.
@ -3327,18 +3227,14 @@ Next, import the `AdminModule` into `app.module.ts` and add it to the `imports`
to register the admin routes.
<code-example path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (admin module)" region="admin-module">
</code-example>
<code-example path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (admin module)" region="admin-module"></code-example>
Add an "Admin" link to the `AppComponent` shell so that users can get to this feature.
<code-example path="router/src/app/app.component.5.html" linenums="false" header="src/app/app.component.html (template)">
</code-example>
<code-example path="router/src/app/app.component.5.html" header="src/app/app.component.html (template)"></code-example>
@ -3367,9 +3263,7 @@ At the moment you're interested in seeing how guards work so the first version d
It simply logs to console and `returns` true immediately, allowing navigation to proceed:
<code-example path="router/src/app/auth/auth.guard.1.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)">
</code-example>
<code-example path="router/src/app/auth/auth.guard.1.ts" header="src/app/auth/auth.guard.ts (excerpt)"></code-example>
@ -3377,9 +3271,7 @@ Next, open `admin-routing.module.ts `, import the `AuthGuard` class, and
update the admin route with a `canActivate` guard property that references it:
<code-example path="router/src/app/admin/admin-routing.module.2.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route">
</code-example>
<code-example path="router/src/app/admin/admin-routing.module.2.ts" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route"></code-example>
@ -3401,9 +3293,7 @@ The `AuthGuard` should call an application service that can login a user and ret
Update the `AuthService` to log in the user:
<code-example path="router/src/app/auth/auth.service.ts" linenums="false" header="src/app/auth/auth.service.ts (excerpt)">
</code-example>
<code-example path="router/src/app/auth/auth.service.ts" header="src/app/auth/auth.service.ts (excerpt)"></code-example>
@ -3416,9 +3306,7 @@ The `redirectUrl` property will store the attempted URL so you can navigate to i
Revise the `AuthGuard` to call it.
<code-example path="router/src/app/auth/auth.guard.2.ts" linenums="false" header="src/app/auth/auth.guard.ts (v2)">
</code-example>
<code-example path="router/src/app/auth/auth.guard.2.ts" header="src/app/auth/auth.guard.ts (v2)"></code-example>
@ -3496,9 +3384,7 @@ async checks and a `boolean` for sync checks.
This one returns a `boolean`:
<code-example path="router/src/app/auth/auth.guard.3.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child">
</code-example>
<code-example path="router/src/app/auth/auth.guard.3.ts" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child"></code-example>
@ -3506,9 +3392,7 @@ Add the same `AuthGuard` to the `component-less` admin route to protect all othe
instead of adding the `AuthGuard` to each route individually.
<code-example path="router/src/app/admin/admin-routing.module.3.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child">
</code-example>
<code-example path="router/src/app/admin/admin-routing.module.3.ts" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child"></code-example>
@ -3557,9 +3441,7 @@ discards the changes when the user presses the *Cancel* button.
Both buttons navigate back to the crisis list after save or cancel.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save"></code-example>
@ -3593,9 +3475,7 @@ Generate a `Dialog` service to handle user confirmation.
Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent. The `window.confirm` is a _blocking_ action that displays a modal dialog and waits for user interaction.
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts">
</code-example>
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts"></code-example>
It returns an `Observable` that *resolves* when the user eventually decides what to do: either
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
@ -3617,9 +3497,7 @@ It need only detect that the component has a `canDeactivate()` method and call i
This approach makes the guard reusable.
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts">
</code-example>
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts"></code-example>
@ -3632,18 +3510,14 @@ 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.
<code-example path="router/src/app/can-deactivate.guard.1.ts" linenums="false" header="src/app/can-deactivate.guard.ts (component-specific)">
</code-example>
<code-example path="router/src/app/can-deactivate.guard.1.ts" header="src/app/can-deactivate.guard.ts (component-specific)"></code-example>
Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate"></code-example>
@ -3656,9 +3530,7 @@ 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 property.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)"></code-example>
Now you have given the user a safeguard against unsaved changes.
@ -3704,9 +3576,7 @@ Generate a `CrisisDetailResolver` service file within the `Crisis Center` featur
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)"></code-example>
@ -3728,17 +3598,13 @@ Observable completes after retrieving the first value from the Observable return
If it doesn't return a valid `Crisis`, return an empty `Observable`, canceling the previous in-flight navigation to the `CrisisDetailComponent` and navigate the user back to the `CrisisListComponent`. The update resolver service looks like this:
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts"></code-example>
Import this resolver in the `crisis-center-routing.module.ts`
and add a `resolve` object to the `CrisisDetailComponent` route configuration.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)"></code-example>
@ -3748,9 +3614,7 @@ that's where you said it should be when you re-configured the route.
It will be there when the `CrisisDetailComponent` ask for it.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit">
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
@ -3853,9 +3717,7 @@ 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.
<code-example path="router/src/app/auth/auth.guard.4.ts" linenums="false" header="src/app/auth/auth.guard.ts (v3)">
</code-example>
<code-example path="router/src/app/auth/auth.guard.4.ts" header="src/app/auth/auth.guard.ts (v3)"></code-example>
@ -3866,9 +3728,7 @@ and provide the `queryParamsHandling` and `preserveFragment` to pass along the c
and fragment to the next route.
<code-example path="router/src/app/auth/login/login.component.ts" linenums="false" header="src/app/auth/login/login.component.ts (preserve)" region="preserve">
</code-example>
<code-example path="router/src/app/auth/login/login.component.ts" header="src/app/auth/login/login.component.ts (preserve)" region="preserve"></code-example>
<div class="alert is-helpful">
@ -3885,9 +3745,7 @@ As you'll be navigating to the *Admin Dashboard* route after logging in, you'll
query parameters and fragment.
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)">
</code-example>
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)"></code-example>
@ -3959,9 +3817,7 @@ The `loadChildren` property takes a function that returns a promise using the br
The path is the location of the `AdminModule` (relative to the app root).
After the code is requested and loaded, the `Promise` resolves an object that contains the `NgModule`, in this case the `AdminModule`.
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)">
</code-example>
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)"></code-example>
<div class="alert is-important">
@ -4020,9 +3876,7 @@ Add it to the `AuthGuard` class's `implements` list.
Then implement `canLoad()` as follows:
<code-example path="router/src/app/auth/auth.guard.ts" linenums="false" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad">
</code-example>
<code-example path="router/src/app/auth/auth.guard.ts" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad"></code-example>
@ -4034,9 +3888,7 @@ array property for the `admin` route.
The completed admin route looks like this:
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)">
</code-example>
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)"></code-example>
@ -4135,9 +3987,7 @@ The second argument in the `RouterModule.forRoot()` method takes an object for a
The `preloadingStrategy` is one of those options.
Add the `PreloadAllModules` token to the `forRoot()` call:
<code-example path="router/src/app/app-routing.module.6.ts" linenums="false" header="src/app/app-routing.module.ts (preload all)" region="forRoot">
</code-example>
<code-example path="router/src/app/app-routing.module.6.ts" header="src/app/app-routing.module.ts (preload all)" region="forRoot"></code-example>
@ -4182,9 +4032,7 @@ Recall that you can add anything to the `data` property of a route.
Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModule`.
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2">
</code-example>
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2"></code-example>
Generate a new `SelectivePreloadingStrategy` service.
@ -4193,9 +4041,7 @@ Generate a new `SelectivePreloadingStrategy` service.
</code-example>
<code-example path="router/src/app/selective-preloading-strategy.service.ts" linenums="false" header="src/app/selective-preloading-strategy.service.ts (excerpt)">
</code-example>
<code-example path="router/src/app/selective-preloading-strategy.service.ts" header="src/app/selective-preloading-strategy.service.ts (excerpt)"></code-example>
@ -4233,9 +4079,7 @@ Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
When you're done it looks like this.
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)">
</code-example>
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)"></code-example>
@ -4257,9 +4101,7 @@ You've setup the routes for navigating around your application. You've used navi
Let's take the `Hero` routes and migrate them to new URLs. The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed. To support this change, you'll add redirects from the old routes to the new routes in the `heroes-routing.module`.
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)">
</code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.ts" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)"></code-example>
You'll notice two different types of redirects. The first change is from `/heroes` to `/superheroes` without any parameters. This is a straightforward redirect, unlike the change from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter. Router redirects also use powerful pattern matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination. Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
@ -4277,21 +4119,15 @@ Before updating the `app-routing.module.ts`, you'll need to consider an importan
So instead, you'll update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (superheroes redirect)">
</code-example>
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (superheroes redirect)"></code-example>
`RouterLink`s aren't tied to route configuration, so you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink.
<code-example path="router/src/app/app.component.html" linenums="false" header="src/app/app.component.html (superheroes active routerLink)">
</code-example>
<code-example path="router/src/app/app.component.html" header="src/app/app.component.html (superheroes active routerLink)"></code-example>
Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" linenums="false" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)">
</code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)"></code-example>
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
@ -4312,9 +4148,7 @@ examining its `config` property.
For example, update the `AppModule` as follows and look in the browser console window
to see the finished route configuration.
<code-example path="router/src/app/app.module.7.ts" linenums="false" header="src/app/app.module.ts (inspect the router config)" region="inspect-config">
</code-example>
<code-example path="router/src/app/app.module.7.ts" header="src/app/app.module.ts (inspect the router config)" region="inspect-config"></code-example>
{@a final-app}
@ -4353,27 +4187,21 @@ A link parameters array holds the following ingredients for router navigation:
You can bind the `RouterLink` directive to such an array like this:
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (h-anchor)" region="h-anchor">
</code-example>
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (h-anchor)" region="h-anchor"></code-example>
You've written a two element array when specifying a route parameter like this:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail">
</code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail"></code-example>
You can provide optional route parameters in an object like this:
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params">
</code-example>
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params"></code-example>
@ -4383,9 +4211,7 @@ The moment you add a child router, such as the crisis center, you create new lin
Recall that you specified a default child route for the crisis center so this simple `RouterLink` is fine.
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default">
</code-example>
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default"></code-example>
@ -4401,9 +4227,7 @@ Take it a step further. Consider the following router link that
navigates from the root of the application down to the *Dragon Crisis*:
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor">
</code-example>
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor"></code-example>
@ -4418,9 +4242,7 @@ 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:
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
@ -4553,9 +4375,7 @@ 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.
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
#### HTML5 URLs and the *&lt;base href>*
@ -4568,9 +4388,7 @@ The preferred way to configure the strategy is to add a
tag in the `<head>` of the `index.html`.
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
@ -4596,6 +4414,4 @@ providing the `useHash: true` in an object as the second argument of the `Router
in the `AppModule`.
<code-example path="router/src/app/app.module.6.ts" linenums="false" header="src/app/app.module.ts (hash URL strategy)">
</code-example>
<code-example path="router/src/app/app.module.6.ts" header="src/app/app.module.ts (hash URL strategy)"></code-example>

View File

@ -32,7 +32,7 @@ The context also defines a *merge strategy* that determines how changes are merg
When you create a new blank schematic with the [Schematics CLI](#cli), the generated entry function is a *rule factory*.
A `RuleFactory`object defines a higher-order function that creates a `Rule`.
<code-example language="TypeScript" linenums="false">
<code-example language="TypeScript">
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
// You don't have to export the function as default.
@ -49,7 +49,7 @@ You need a rule, for example, to define how a template in the schematic is to be
Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.
<code-example language="none" linenums="false">
<code-example language="none">
import {
JsonAstObject,
@ -78,7 +78,7 @@ You can see examples of schema files for the Angular CLI command schematics in [
Schematics come with their own command-line tool.
Using Node 6.9 or above, install the Schematics command line tool globally:
<code-example language="bash" linenums="false">
<code-example language="bash">
npm install -g @angular-devkit/schematics-cli
</code-example>
@ -94,7 +94,7 @@ See [Schematics for Libraries](guide/schematics-for-libraries).
The following command creates a new schematic named `hello-world` in a new project folder of the same name.
<code-example language="bash" linenums="false">
<code-example language="bash">
schematics blank --name=hello-world
</code-example>
@ -102,7 +102,7 @@ The `blank` schematic is provided by the Schematics CLI. The command creates a n
Go to the collection folder, install your npm dependencies, and open your new collection in your favorite editor to see the generated files. For example, if you are using VSCode:
<code-example language="bash" linenums="false">
<code-example language="bash">
cd hello-world
npm install
npm run build
@ -118,14 +118,14 @@ Each schematic name must be unique within the collection.
Use the `schematics` command to run a named schematic.
Provide the path to the project folder, the schematic name, and any mandatory options, in the following format.
<code-example language="bash" linenums="false">
<code-example language="bash">
schematics &lt;path-to-schematics-project&gt;:&lt;schematics-name&gt; --&lt;required-option&gt;=&lt;value&gt;
</code-example>
The path can be absolute or relative to the current working directory where the command is executed.
For example, to run the schematic we just generated (which has no required options), use the following command.
<code-example language="bash" linenums="false">
<code-example language="bash">
schematics .:hello-world
</code-example>
@ -133,7 +133,7 @@ schematics .:hello-world
To add a schematic to an existing collection, use the same command you use to start a new schematics project, but run the command inside the project folder.
<code-example language="bash" linenums="false">
<code-example language="bash">
cd hello-world
schematics blank --name=goodbye-world
</code-example>
@ -147,7 +147,7 @@ The top level of the root project folder for a collection contains configuration
The `src/` folder contains subfolders for named schematics in the collection, and a schema, `collection.json`, which describes the collected schematics.
Each schematic is created with a name, description, and factory function.
<code-example language="none" linenums="false">
<code-example language="none">
{
"$schema":
"../node_modules/@angular-devkit/schematics/collection-schema.json",

View File

@ -93,7 +93,7 @@ You can add a named schematic to your collection that lets your users use the `n
We'll assume that your library defines a service, `my-service`, that requires some setup. You want your users to be able to generate it using the following CLI command.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng generate my-lib:my-service
</code-example>
@ -276,7 +276,7 @@ After you build your library and schematics, you can install the schematics coll
From the root of your workspace, run the `ng build` command for your library.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng build my-lib
@ -284,7 +284,7 @@ From the root of your workspace, run the `ng build` command for your library.
Then, you change into your library directory to build the schematic
<code-example language="bash" linenums="false">
<code-example language="bash">
cd projects/my-lib
npm run build
@ -295,7 +295,7 @@ Then, you change into your library directory to build the schematic
Your library and schematics are packaged and placed in the `dist/my-lib` folder at the root of your workspace. For running the schematic, you need to link the library into your `node_modules` folder. From the root of your workspace, run the `npm link` command with the path to your distributable library.
<code-example language="bash" linenums="false">
<code-example language="bash">
npm link dist/my-lib
@ -305,7 +305,7 @@ npm link dist/my-lib
Now that your library is installed, you can run the schematic using the `ng generate` command.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng generate my-lib:my-service --name my-data
@ -313,7 +313,7 @@ ng generate my-lib:my-service --name my-data
In the console, you will see that the schematic was run and the `my-data.service.ts` file was created in your app folder.
<code-example language="bash" linenums="false" hideCopy="true">
<code-example language="bash" hideCopy="true">
CREATE src/app/my-data.service.ts (208 bytes)

View File

@ -17,13 +17,13 @@ Schematics that are included in the `@schematics/angular` collection are run by
The package contains named schematics that configure the options that are available to the CLI for `ng generate` sub-commands, such as `ng generate component` and `ng generate service`.
The subcommands for `ng generate` are shorthand for the corresponding schematic. You can specify a particular schematic (or collection of schematics) to generate, using the long form:
<code-example language="bash" linenums="false">
<code-example language="bash">
ng generate my-schematic-collection:my-schematic-name
</code-example>
&mdash;or&mdash;
<code-example language="bash" linenums="false">
<code-example language="bash">
ng generate my-schematic-name --collection collection-name
</code-example>
@ -72,7 +72,7 @@ The documented sub-commands use the default Angular generation schematics, but y
Angular Material, for example, supplies generation schematics for the UI components that it defines.
The following command uses one of these schematics to render an Angular Material `<mat-table>` that is pre-configured with a datasource for sorting and pagination.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng generate @angular/material:table <component-name>
</code-example>
@ -80,7 +80,7 @@ ng generate @angular/material:table <component-name>
The `ng update` command can be used to update your workspace's library dependencies. If you supply no options or use the help option, the command examines your workspace and suggests libraries to update.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng update
We analyzed your package.json, there are some packages to update:
@ -113,7 +113,7 @@ If you create a new version of your library that introduces potential breaking c
For example, suppose you want to update the Angular Material library.
<code-example language="bash" linenums="false">
<code-example language="bash">
ng update @angular/material
</code-example>

View File

@ -98,9 +98,7 @@ The following template binds the value of `htmlSnippet`, once by interpolating i
content, and once by binding it to the `innerHTML` property of an element:
<code-example path="security/src/app/inner-html-binding.component.html" header="src/app/inner-html-binding.component.html">
</code-example>
<code-example path="security/src/app/inner-html-binding.component.html" header="src/app/inner-html-binding.component.html"></code-example>
@ -112,9 +110,7 @@ a value that an attacker might control into `innerHTML` normally causes an XSS
vulnerability. For example, code contained in a `<script>` tag is executed:
<code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" header="src/app/inner-html-binding.component.ts (class)" region="class">
</code-example>
<code-example path="security/src/app/inner-html-binding.component.ts" header="src/app/inner-html-binding.component.ts (class)" region="class"></code-example>
@ -200,9 +196,7 @@ your intended use of the value. Imagine that the following template needs to bin
`javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" header="src/app/bypass-security.component.html (URL)" region="URL">
</code-example>
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (URL)" region="URL"></code-example>
@ -211,9 +205,7 @@ in development mode, logs this action to the console. To prevent
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example>
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url"></code-example>
@ -231,15 +223,11 @@ could execute. So call a method on the controller to construct a trusted video U
Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" header="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example>
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (iframe)" region="iframe"></code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example>
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"></code-example>

View File

@ -24,7 +24,7 @@ The `SwUpdate` service supports four separate operations:
The two update events, `available` and `activated`, are `Observable` properties of `SwUpdate`:
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" linenums="false" header="log-update.service.ts" region="sw-update"> </code-example>
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" header="log-update.service.ts" region="sw-update"></code-example>
You can use these events to notify the user of a pending update or to refresh their pages when the code they are running is out of date.
@ -35,7 +35,7 @@ It's possible to ask the service worker to check if any updates have been deploy
Do this with the `checkForUpdate()` method:
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" linenums="false" header="check-for-update.service.ts"> </code-example>
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" header="check-for-update.service.ts"></code-example>
This method returns a `Promise` which indicates that the update check has completed successfully, though it does not indicate whether an update was discovered as a result of the check. Even if one is found, the service worker must still successfully download the changed files, which can fail. If successful, the `available` event will indicate availability of a new version of the app.
@ -51,7 +51,7 @@ You can avoid that by waiting for the app to stabilize first, before starting to
(as shown in the example above).
Note that this is true for any kind of polling done by your application.
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
</div>
@ -59,7 +59,7 @@ Check the {@link ApplicationRef#isStable isStable} documentation for more inform
If the current tab needs to be updated to the latest app version immediately, it can ask to do so with the `activateUpdate()` method:
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" linenums="false" header="prompt-update.service.ts" region="sw-activate"> </code-example>
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" header="prompt-update.service.ts" region="sw-activate"></code-example>
Doing this could break lazy-loading into currently running apps, especially if the lazy-loaded chunks use filenames with hashes, which change every version.

View File

@ -43,7 +43,7 @@ for getting and setting the current HTML document title:
You can inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
<code-example path="set-document-title/src/app/app.component.ts" region="class" header="src/app/app.component.ts (class)" linenums="false"></code-example>
<code-example path="set-document-title/src/app/app.component.ts" region="class" header="src/app/app.component.ts (class)"></code-example>
Bind that method to three anchor tags and voilà!

View File

@ -20,7 +20,7 @@ There are two ways to make a service a singleton in Angular:
Beginning with Angular 6.0, the preferred way to create a singleton service is to set `providedIn` to `root` on the service's `@Injectable()` decorator. This tells Angular
to provide the service in the application root.
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
[Tour of Heroes tutorial](tutorial).
@ -70,7 +70,7 @@ with `providers` and child modules without `providers`.
1. Create a static method `forRoot()` on the module.
2. Place the providers into the `forRoot()` method.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts" linenums="false"> </code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts"></code-example>
{@a forRoot-router}
@ -117,22 +117,16 @@ the greeting `UserService`.
In the following example, the optional, injected `UserServiceConfig`
extends the greeting `UserService`. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)" linenums="false">
</code-example>
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)"></code-example>
Here's `forRoot()` that takes a `UserServiceConfig` object:
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)" linenums="false">
</code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)"></code-example>
Lastly, call it within the `imports` list of the `AppModule`. In the following
snippet, other parts of the file are left out. For the complete file, see the <live-example name="ngmodules"></live-example>, or continue to the next section of this document.
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)" linenums="false">
</code-example>
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)"></code-example>
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
@ -146,9 +140,7 @@ lazy-loaded module imports it too, the app can generate
To guard against a lazy loaded module re-importing `GreetingModule`, add the following `GreetingModule` constructor.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts" linenums="false">
</code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts"></code-example>
The constructor tells Angular to inject the `GreetingModule` into itself.
The injection would be circular if Angular looked for
@ -172,7 +164,7 @@ Now `parentModule` exists and the constructor throws the error.
Here are the two files in their entirety for reference:
<code-tabs linenums="false">
<code-tabs>
<code-pane header="app.module.ts" path="ngmodules/src/app/app.module.ts">
</code-pane>
<code-pane header="greeting.module.ts" region="whole-greeting-module" path="ngmodules/src/app/greeting/greeting.module.ts">

View File

@ -31,9 +31,7 @@ Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
@ -52,9 +50,7 @@ described in the [_Template Syntax_](guide/template-syntax) guide and seen in sa
Here's an example of them in a template:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (built-in)" region="built-in">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (built-in)" region="built-in"></code-example>
@ -121,9 +117,7 @@ You can [only apply one](guide/structural-directives#one-per-element) _structura
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-true)" region="ngif-true">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-true)" region="ngif-true"></code-example>
@ -150,9 +144,7 @@ The component and DOM nodes can be garbage-collected and free up memory.
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (display-none)" region="display-none">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (display-none)" region="display-none"></code-example>
@ -201,9 +193,7 @@ and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (asterisk)" region="asterisk">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (asterisk)" region="asterisk"></code-example>
@ -211,9 +201,7 @@ The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular translates the `*ngIf` _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-template)" region="ngif-template">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-template)" region="ngif-template"></code-example>
@ -246,9 +234,7 @@ Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax to `
Here's a full-featured application of `NgFor`, written both ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"></code-example>
@ -499,9 +485,7 @@ The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`,
Here's an example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch)" region="ngswitch"></code-example>
@ -535,9 +519,7 @@ As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the `<ng-template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"></code-example>
@ -570,9 +552,7 @@ those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (template-tag)" region="template-tag">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (template-tag)" region="template-tag"></code-example>
@ -602,9 +582,7 @@ There's often a _root_ element that can and should host the structural directive
The list element (`<li>`) is a typical host element of an `NgFor` repeater.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngfor-li)" region="ngfor-li">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngfor-li)" region="ngfor-li"></code-example>
@ -612,9 +590,7 @@ When there isn't a host element, you can usually wrap the content in a native HT
such as a `<div>`, and attach the directive to that wrapper.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
@ -627,18 +603,14 @@ neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-span)" region="ngif-span">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-span)" region="ngif-span"></code-example>
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
<code-example path="structural-directives/src/app/app.component.css" linenums="false" header="src/app/app.component.css (p-span)" region="p-span">
</code-example>
<code-example path="structural-directives/src/app/app.component.css" header="src/app/app.component.css (p-span)" region="p-span"></code-example>
@ -660,9 +632,7 @@ You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this,
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (select-span)" region="select-span">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-span)" region="select-span"></code-example>
@ -685,9 +655,7 @@ because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"></code-example>
@ -703,9 +671,7 @@ It renders properly.
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"></code-example>
@ -756,9 +722,7 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (appUnless-1)" region="appUnless-1">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless-1)" region="appUnless-1"></code-example>
@ -775,9 +739,7 @@ Creating a directive is similar to creating a component.
Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (skeleton)" region="skeleton">
</code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (skeleton)" region="skeleton"></code-example>
@ -810,9 +772,7 @@ and access the _view container_ through a
You inject both in the directive constructor as private variables of the class.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (ctor)" region="ctor">
</code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (ctor)" region="ctor"></code-example>
@ -833,9 +793,7 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax#inputs-outp
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (set)" region="set">
</code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (set)" region="set"></code-example>
@ -853,9 +811,7 @@ Nobody reads the `appUnless` property so it doesn't need a getter.
The completed directive code looks like this:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (excerpt)" region="no-docs"></code-example>
@ -864,9 +820,7 @@ Add this directive to the `declarations` array of the AppModule.
Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (appUnless)" region="appUnless">
</code-example>
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless)" region="appUnless"></code-example>

View File

@ -3670,7 +3670,7 @@ Compare with the less preferred `host` metadata alternative.
</div>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/treeshaking/service.ts" linenums="false"> </code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/treeshaking/service.ts"></code-example>

View File

@ -58,15 +58,13 @@ By default, interpolation uses as its delimiter the double curly braces, `{{` an
In the following snippet, `{{ currentCustomer }}` is an example of interpolation.
<code-example path="interpolation/src/app/app.component.html" region="interpolation-example1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="interpolation-example1" header="src/app/app.component.html"></code-example>
The text between the braces is often the name of a component
property. Angular replaces that name with the
string value of the corresponding component property.
<code-example path="interpolation/src/app/app.component.html" region="component-property" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="component-property" header="src/app/app.component.html"></code-example>
In the example above, Angular evaluates the `title` and `itemImageUrl` properties
and fills in the blanks, first displaying some title text and then an image.
@ -75,14 +73,12 @@ More generally, the text between the braces is a **template expression**
that Angular first **evaluates** and then **converts to a string**.
The following interpolation illustrates the point by adding two numbers:
<code-example path="interpolation/src/app/app.component.html" region="convert-string" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="convert-string" header="src/app/app.component.html"></code-example>
The expression can invoke methods of the host component such as `getVal()` in
the following example:
<code-example path="interpolation/src/app/app.component.html" region="invoke-method" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="invoke-method" header="src/app/app.component.html"></code-example>
Angular evaluates all expressions in double curly braces,
converts the expression results to strings, and links them with neighboring literal strings. Finally,
@ -137,8 +133,7 @@ The *expression context* is typically the _component_ instance.
In the following snippets, the `recommended` within double curly braces and the
`itemImageUrl2` in quotes refer to properties of the `AppComponent`.
<code-example path="interpolation/src/app/app.component.html" region="component-context" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="component-context" header="src/app/app.component.html"></code-example>
An expression may also refer to properties of the _template's_ context
such as a template input variable,
@ -146,11 +141,9 @@ such as a template input variable,
`let customer`, or a template reference variable, `#customerInput`.
<!-- link to guide/template-ref-variables -->
<code-example path="interpolation/src/app/app.component.html" region="template-input-variable" header="src/app/app.component.html (template input variable)" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="template-input-variable" header="src/app/app.component.html (template input variable)"></code-example>
<code-example path="interpolation/src/app/app.component.html" region="template-reference-variable" header="src/app/app.component.html (template reference variable)" linenums="false">
</code-example>
<code-example path="interpolation/src/app/app.component.html" region="template-reference-variable" header="src/app/app.component.html (template reference variable)"></code-example>
The context for terms in an expression is a blend of the _template variables_,
the directive's _context_ object (if it has one), and the component's _members_.
@ -241,8 +234,7 @@ such as an element, component, or directive.
You'll see template statements in the [event binding](guide/template-syntax#event-binding) section,
appearing in quotes to the right of the `=`&nbsp;symbol as in `(event)="statement"`.
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
A template statement *has a side effect*.
That's the whole point of an event.
@ -272,8 +264,7 @@ such as an event handling method of the component instance.
The *statement context* is typically the component instance.
The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component.
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
The statement context may also refer to properties of the template's own context.
In the following examples, the template `$event` object,
@ -281,8 +272,7 @@ a [template input variable](guide/template-syntax#template-input-variable) (`let
and a [template reference variable](guide/template-syntax#ref-vars) (`#heroForm`)
are passed to an event handling method of the component.
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
Template context names take precedence over component context names.
In `deleteHero(hero)` above, the `hero` is the template input variable,
@ -419,8 +409,7 @@ you modify those elements by setting element attributes with string constants.
With data-binding, you can control things like the state of a button:
<code-example path="binding-syntax/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="binding-syntax/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html"></code-example>
Notice that the binding is to the `disabled` property of the button's DOM element,
**not** the attribute. This applies to data-binding in general. Data-binding works with *properties* of DOM elements, components, and directives, not HTML *attributes*.
@ -564,8 +553,7 @@ The following table summarizes:
</td>
<td>
<code>src</code>, <code>hero</code>, and <code>ngClass</code> in the following:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-syntax-1"></code-example>
<!-- For more information, see [Property Binding](guide/property-binding). -->
</td>
</tr>
@ -580,8 +568,7 @@ The following table summarizes:
</td>
<td>
<code>click</code>, <code>deleteRequest</code>, and <code>myClick</code> in the following:
<code-example path="template-syntax/src/app/app.component.html" region="event-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="event-binding-syntax-1"></code-example>
<!-- KW--Why don't these links work in the table? -->
<!-- <div>For more information, see [Event Binding](guide/event-binding).</div> -->
</td>
@ -594,8 +581,7 @@ The following table summarizes:
Event and property
</td>
<td>
<code-example path="template-syntax/src/app/app.component.html" region="2-way-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="2-way-binding-syntax-1"></code-example>
</td>
</tr>
<tr>
@ -607,8 +593,7 @@ The following table summarizes:
(the&nbsp;exception)
</td>
<td>
<code-example path="template-syntax/src/app/app.component.html" region="attribute-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="attribute-binding-syntax-1"></code-example>
</td>
</tr>
<tr>
@ -619,8 +604,7 @@ The following table summarizes:
<code>class</code> property
</td>
<td>
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-syntax-1"></code-example>
</td>
</tr>
<tr>
@ -631,8 +615,7 @@ The following table summarizes:
<code>style</code> property
</td>
<td>
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-syntax-1" linenums="false">
</code-example>
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-syntax-1"></code-example>
</td>
</tr>
</table>
@ -670,14 +653,12 @@ The most common property binding sets an element property to a component
property value. An example is
binding the `src` property of an image element to a component's `itemImageUrl` property:
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
Here's an example of binding to the `colSpan` property. Notice that it's not `colspan`,
which is the attribute, spelled with a lowercase `s`.
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
For more details, see the [MDN HTMLTableCellElment](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
@ -686,19 +667,16 @@ For more about `colSpan` and `colspan`, see (Attribute Binding)[guide/template-s
Another example is disabling a button when the component says that it `isUnchanged`:
<code-example path="property-binding/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html"></code-example>
Another is setting a property of a directive:
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html"></code-example>
Yet another is setting the model property of a custom component&mdash;a great way
for parent and child components to communicate:
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
### Binding target
@ -706,13 +684,11 @@ An element property between enclosing square brackets identifies
the target property.
The target property in the following code is the image element's `src` property.
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
There's also the `bind-` prefix alternative:
<code-example path="property-binding/src/app/app.component.html" region="bind-prefix" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="bind-prefix" header="src/app/app.component.html"></code-example>
In most cases, the target name is the name of a property, even
@ -723,8 +699,7 @@ Element properties may be the more common targets,
but Angular looks first to see if the name is a property of a known directive,
as it is in the following example:
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html"></code-example>
Technically, Angular is matching the name to a directive `@Input()`,
one of the property names listed in the directive's `inputs` array
@ -769,16 +744,13 @@ expects a number, an object if it expects an object, and so on.
In the following example, the `childItem` property of the `ItemDetailComponent` expects a string, which is exactly what you're sending in the property binding:
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
You can confirm this by looking in the `ItemDetailComponent` where the `@Input` type is set to a string:
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type" linenums="false">
</code-example>
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type"></code-example>
As you can see here, the `parentItem` in `AppComponent` is a string, which the `ItemDetailComponent` expects:
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts"></code-example>
#### Passing in an object
@ -787,24 +759,20 @@ the syntax and thinking are the same.
In this scenario, `ListItemComponent` is nested within `AppComponent` and the `item` property expects an object.
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
The `item` property is declared in the `ListItemComponent` with a type of `Item` and decorated with `@Input()`:
<code-example path="property-binding/src/app/list-item/list-item.component.ts" region="item-input" header="src/app/list-item.component.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/list-item/list-item.component.ts" region="item-input" header="src/app/list-item.component.ts"></code-example>
In this sample app, an `Item` is an object that has two properties; an `id` and a `name`.
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts"></code-example>
While a list of items exists in another file, `mock-items.ts`, you can
specify a different item in `app.component.ts` so that the new item will render:
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
You just have to make sure, in this case, that you're supplying an object because that's the type of `item` and is what the nested component, `ListItemComponent`, expects.
@ -818,8 +786,7 @@ The brackets, `[]`, tell Angular to evaluate the template expression.
If you omit the brackets, Angular treats the string as a constant
and *initializes the target property* with that string:
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html"></code-example>
Omitting the brackets will render the string
@ -838,8 +805,7 @@ just as well for directive and component property initialization.
The following example initializes the `prefix` property of the `StringInitComponent` to a fixed string,
not a template expression. Angular sets it and forgets about it.
<code-example path="property-binding/src/app/app.component.html" region="string-init" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="string-init" header="src/app/app.component.html"></code-example>
The `[item]` binding, on the other hand, remains a live binding to the component's `currentItem` property.
@ -848,8 +814,7 @@ The `[item]` binding, on the other hand, remains a live binding to the component
You often have a choice between interpolation and property binding.
The following binding pairs do the same thing:
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
Interpolation is a convenient alternative to property binding in
many cases. When rendering data values as strings, there is no
@ -861,13 +826,11 @@ property to a non-string data value, you must use property binding*.
Imagine the following malicious content.
<code-example path="property-binding/src/app/app.component.ts" region="malicious-content" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.ts" region="malicious-content" header="src/app/app.component.ts"></code-example>
In the component template, the content might be used with interpolation:
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.ts"></code-example>
Fortunately, Angular data binding is on alert for dangerous HTML. In the above case,
the HTML displays as is, and the Javascript does not execute. Angular **does not**
@ -877,8 +840,7 @@ nor property binding.
In the following example, however, Angular [sanitizes](guide/security#sanitization-and-security-contexts)
the values before displaying them.
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html"></code-example>
Interpolation handles the `<script>` tags differently than
property binding but both approaches render the
@ -920,8 +882,7 @@ or remove the attribute when the expression resolves to `null`.
One of the primary use cases for attribute binding
is to set ARIA attributes, as in this example:
<code-example path="attribute-binding/src/app/app.component.html" region="attrib-binding-aria" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="attrib-binding-aria" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -948,8 +909,7 @@ corresponding property. Interpolation and property binding can set only *propert
Instead, you'd use property binding and write it like this:
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
</div>
@ -974,20 +934,17 @@ optionally followed by a dot (`.`) and the name of a CSS class: `[class.class-na
You can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
<code-example path="attribute-binding/src/app/app.component.html" region="class-override" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="class-override" header="src/app/app.component.html"></code-example>
You can also add append a class to an element without overwriting the classes already on the element:
<code-example path="attribute-binding/src/app/app.component.html" region="add-class" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="add-class" header="src/app/app.component.html"></code-example>
Finally, you can bind to a specific class name.
Angular adds the class when the template expression evaluates to truthy.
It removes the class when the expression is falsy.
<code-example path="attribute-binding/src/app/app.component.html" region="is-special" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="is-special" header="src/app/app.component.html"></code-example>
While this technique is suitable for toggling a single class name,
consider the [`NgClass`](guide/template-syntax#ngClass) directive when
@ -1004,14 +961,12 @@ Style binding syntax resembles property binding.
Instead of an element property between brackets, start with the prefix `style`,
followed by a dot (`.`) and the name of a CSS style property: `[style.style-property]`.
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding" header="src/app/app.component.html"></code-example>
Some style binding styles have a unit extension.
The following example conditionally sets the font size in “em” and “%” units .
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html"></code-example>
**This technique is suitable for setting a single style, but consider
the [`NgStyle`](guide/template-syntax#ngStyle) directive when setting several inline styles at the same time.**
@ -1048,19 +1003,16 @@ the component's `onSave()` method whenever a click occurs:
As above, the target is the button's click event.
<code-example path="event-binding/src/app/app.component.html" region="event-binding-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="event-binding/src/app/app.component.html" region="event-binding-1" header="src/app/app.component.html"></code-example>
Alternatively, use the `on-` prefix, known as the canonical form:
<code-example path="event-binding/src/app/app.component.html" region="event-binding-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="event-binding/src/app/app.component.html" region="event-binding-2" header="src/app/app.component.html"></code-example>
Element events may be the more common targets, but Angular looks first to see if the name matches an event property
of a known directive, as it does in the following example:
<code-example path="event-binding/src/app/app.component.html" region="custom-directive" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="event-binding/src/app/app.component.html" region="custom-directive" header="src/app/app.component.html"></code-example>
If the name fails to match an element event or an output property of a known directive,
Angular reports an “unknown directive” error.
@ -1084,8 +1036,7 @@ with properties such as `target` and `target.value`.
Consider this example:
<code-example path="event-binding/src/app/app.component.html" region="event-binding-3" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="event-binding/src/app/app.component.html" region="event-binding-3" header="src/app/app.component.html"></code-example>
This code sets the `<input>` `value` property by binding to the `name` property.
To listen for changes to the value, the code binds to the `input`
@ -1112,11 +1063,9 @@ Although the `ItemDetailComponent` has a delete button, it doesn't know how to d
Here are the pertinent excerpts from that `ItemDetailComponent`:
<code-example path="event-binding/src/app/item-detail/item-detail.component.html" linenums="false" header="src/app/item-detail/item-detail.component.html (template)" region="line-through">
</code-example>
<code-example path="event-binding/src/app/item-detail/item-detail.component.html" header="src/app/item-detail/item-detail.component.html (template)" region="line-through"></code-example>
<code-example path="event-binding/src/app/item-detail/item-detail.component.ts" linenums="false" header="src/app/item-detail/item-detail.component.ts (deleteRequest)" region="deleteRequest">
</code-example>
<code-example path="event-binding/src/app/item-detail/item-detail.component.ts" header="src/app/item-detail/item-detail.component.ts (deleteRequest)" region="deleteRequest"></code-example>
The component defines a `deleteRequest` property that returns an `EventEmitter`.
@ -1126,8 +1075,7 @@ telling the `EventEmitter` to emit an `Item` object.
Now imagine a hosting parent component that binds to the `deleteRequest` event
of the `ItemDetailComponent`.
<code-example path="event-binding/src/app/app.component.html" linenums="false" header="src/app/app.component.html (event-binding-to-component)" region="event-binding-to-component">
</code-example>
<code-example path="event-binding/src/app/app.component.html" header="src/app/app.component.html (event-binding-to-component)" region="event-binding-to-component"></code-example>
When the `deleteRequest` event fires, Angular calls the parent component's
`deleteItem()` method, passing the *item-to-delete* (emitted by `ItemDetail`)
@ -1181,8 +1129,7 @@ property called `x` and a corresponding event named `xChange`.
Here's a `SizerComponent` that fits this pattern.
It has a `size` value property and a companion `sizeChange` event:
<code-example path="two-way-binding/src/app/sizer/sizer.component.ts" header="src/app/sizer.component.ts" linenums="false">
</code-example>
<code-example path="two-way-binding/src/app/sizer/sizer.component.ts" header="src/app/sizer.component.ts"></code-example>
The initial `size` is an input value from a property binding.
Clicking the buttons increases or decreases the `size`, within
@ -1191,13 +1138,11 @@ and then raises, or emits, the `sizeChange` event with the adjusted size.
Here's an example in which the `AppComponent.fontSizePx` is two-way bound to the `SizerComponent`:
<code-example path="two-way-binding/src/app/app.component.html" linenums="false" header="src/app/app.component.html (two-way-1)" region="two-way-1">
</code-example>
<code-example path="two-way-binding/src/app/app.component.html" header="src/app/app.component.html (two-way-1)" region="two-way-1"></code-example>
The `AppComponent.fontSizePx` establishes the initial `SizerComponent.size` value.
<code-example path="two-way-binding/src/app/app.component.ts" header="src/app/app.component.ts" region="font-size">
</code-example>
<code-example path="two-way-binding/src/app/app.component.ts" header="src/app/app.component.ts" region="font-size"></code-example>
Clicking the buttons updates the `AppComponent.fontSizePx` via the two-way binding.
The revised `AppComponent.fontSizePx` value flows through to the _style_ binding,
@ -1206,8 +1151,7 @@ making the displayed text bigger or smaller.
The two-way binding syntax is really just syntactic sugar for a _property_ binding and an _event_ binding.
Angular desugars the `SizerComponent` binding into this:
<code-example path="two-way-binding/src/app/app.component.html" linenums="false" header="src/app/app.component.html (two-way-2)" region="two-way-2">
</code-example>
<code-example path="two-way-binding/src/app/app.component.html" header="src/app/app.component.html (two-way-2)" region="two-way-2"></code-example>
The `$event` variable contains the payload of the `SizerComponent.sizeChange` event.
Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the user clicks the buttons.
@ -1261,8 +1205,7 @@ The most common attribute directives are as follows:
Add or remove several CSS classes simultaneously with `ngClass`.
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1275,13 +1218,11 @@ Consider a `setCurrentClasses()` component method that sets a component property
`true`/`false` state of three other component properties. Each key of the object is a CSS class name; its value is `true` if the class should be added,
`false` if it should be removed.
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts"></code-example>
Adding an `ngClass` property binding to `currentClasses` sets the element's classes accordingly:
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1302,8 +1243,7 @@ Use `NgStyle` to set many inline styles simultaneously and dynamically, based on
For context, consider setting a *single* style value with [style binding](guide/template-syntax#style-binding), without `NgStyle`.
<code-example path="built-in-directives/src/app/app.component.html" region="without-ng-style" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="without-ng-style" header="src/app/app.component.html"></code-example>
However, to set *many* inline styles at the same time, use the `NgStyle` directive.
@ -1311,13 +1251,11 @@ The following is a `setCurrentStyles()` method that sets a component
property, `currentStyles`, with an object that defines three styles,
based on the state of three other component properties:
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts"></code-example>
Adding an `ngStyle` property binding to `currentStyles` sets the element's styles accordingly:
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1335,8 +1273,7 @@ Remember to call `setCurrentStyles()`, both initially and when the dependent pro
The `NgModel` directive allows you to display a data property and
update that property when the user makes changes. Here's an example:
<code-example path="built-in-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (NgModel example)" region="NgModel-1">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" header="src/app/app.component.html (NgModel example)" region="NgModel-1"></code-example>
#### Import `FormsModule` to use `ngModel`
@ -1347,20 +1284,17 @@ Learn more about the `FormsModule` and `ngModel` in [Forms](guide/forms#ngModel)
Remember to import the `FormsModule` to make `[(ngModel)]` available as follows:
<code-example path="built-in-directives/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module">
</code-example>
<code-example path="built-in-directives/src/app/app.module.ts" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module"></code-example>
You could achieve the same result with separate bindings to
the `<input>` element's `value` property and `input` event:
<code-example path="built-in-directives/src/app/app.component.html" region="without-NgModel" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="without-NgModel" header="src/app/app.component.html"></code-example>
To streamline the syntax, the `ngModel` directive hides the details behind its own `ngModel` input and `ngModelChange` output properties:
<code-example path="built-in-directives/src/app/app.component.html" region="NgModelChange" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgModelChange" header="src/app/app.component.html"></code-example>
The `ngModel` data property sets the element's value property and the `ngModelChange` event property
listens for changes to the element's value.
@ -1388,15 +1322,13 @@ Separate `ngModel` bindings are an improvement over binding to the
element's native properties, but you can streamline the binding with a
single declaration using the `[(ngModel)]` syntax:
<code-example path="built-in-directives/src/app/app.component.html" region="NgModel-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgModel-1" header="src/app/app.component.html"></code-example>
This `[(ngModel)]` syntax can only _set_ a data-bound property.
If you need to do something more, you can write the expanded form;
for example, the following changes the `<input>` value to uppercase:
<code-example path="built-in-directives/src/app/app.component.html" region="uppercase" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="uppercase" header="src/app/app.component.html"></code-example>
Here are all variations in action, including the uppercase version:
@ -1445,8 +1377,7 @@ You can add or remove an element from the DOM by applying an `NgIf` directive to
a host element.
Bind the directive to a condition expression like `isActive` in this example.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-1" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1469,8 +1400,7 @@ For comparison, the following example shows how to control
the visibility of an element with a
[class](guide/template-syntax#class-binding) or [style](guide/template-syntax#style-binding) binding.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-3" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-3" header="src/app/app.component.html"></code-example>
When you hide an element, that element and all of its descendants remain in the DOM.
All components for those elements stay in memory and
@ -1500,11 +1430,9 @@ The following shows `NgIf` guarding two `<div>`s.
The `currentCustomer` name appears only when there is a `currentCustomer`.
The `nullCustomer` will not be displayed as long as it is `null`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2" header="src/app/app.component.html"></code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2b" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2b" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1523,13 +1451,11 @@ and then you tell Angular to use that block as a template for rendering each ite
Here is an example of `NgFor` applied to a simple `<div>`:
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
You can also apply an `NgFor` to a component element, as in this example:
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html"></code-example>
<div class="alert is-critical">
@ -1573,8 +1499,7 @@ as well as within its descendants to access the item's properties.
The following example references `item` first in an interpolation
and then passes in a binding to the `item` property of the `<app-item-detail>` component.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1-2" header="src/app/app.component.html"></code-example>
For more information about template input variables, see
[Structural Directives](guide/structural-directives#template-input-variable).
@ -1587,8 +1512,7 @@ You can capture the `index` in a template input variable and use it in the templ
The next example captures the `index` in a variable named `i` and displays it with the item name.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-3" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-3" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
@ -1607,13 +1531,11 @@ Add a method to the component that returns the value `NgFor` should track.
In this case, that value is the hero's `id`. If the `id` has already been rendered,
Angular keeps track of it and doesn't re-query the server for the same `id`.
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts"></code-example>
In the microsyntax expression, set `trackBy` to the `trackByItems()` method.
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html"></code-example>
Here is an illustration of the `trackBy` effect.
"Reset items" creates new items with the same `item.id`s.
@ -1646,8 +1568,7 @@ Angular puts only the selected element into the DOM.
`NgSwitch` is actually a set of three, cooperating directives:
`NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault` as in the following example.
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
<figure>
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
@ -1678,8 +1599,7 @@ which is bound to the `currentItem` of the parent component.
Switch directives work as well with native elements and web components too.
For example, you could replace the `<app-best-item>` switch case with the following.
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch-div" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch-div" header="src/app/app.component.html"></code-example>
<hr/>
@ -1702,14 +1622,12 @@ For a demonstration of the syntax and code snippets in this section, see the <li
Use the hash symbol (#) to declare a reference variable.
The following reference variable, `#phone`, declares a `phone` variable on an `<input>` element.
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-var" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-var" header="src/app/app.component.html"></code-example>
You can refer to a template reference variable anywhere in the component's template.
Here, a `<button>` further down the template refers to the `phone` variable.
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-phone" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-phone" header="src/app/app.component.html"></code-example>
<h3 class="no-toc">How a reference variable gets its value</h3>
@ -1720,8 +1638,7 @@ The button's click handler passes the `<input>` value to the component's `callPh
The `NgForm` directive can change that behavior and set the value to something else. In the following example, the template reference variable, `itemForm`, appears three times separated
by HTML.
<code-example path="template-reference-variables/src/app/app.component.html" region="ngForm" header="src/app/hero-form.component.html" linenums="false">
</code-example>
<code-example path="template-reference-variables/src/app/app.component.html" region="ngForm" header="src/app/hero-form.component.html"></code-example>
The reference value of itemForm, without the ngForm attribute value, would be
the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement).
@ -1751,8 +1668,7 @@ You can use the `ref-` prefix alternative to `#`.
This example declares the `fax` variable as `ref-fax` instead of `#fax`.
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-fax" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-fax" header="src/app/app.component.html"></code-example>
<hr/>
@ -1826,8 +1742,7 @@ To illustrate the use of `@Input()`, edit these parts of your app:
To use the `@Input()` decorator in a child component class, first import
`Input` and then decorate the property with `@Input()`:
<code-example path="inputs-outputs/src/app/item-detail/item-detail.component.ts" region="use-input" header="src/app/item-detail/item-detail.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/item-detail/item-detail.component.ts" region="use-input" header="src/app/item-detail/item-detail.component.ts"></code-example>
In this case, `@Input()` decorates the property <code class="no-auto-link">item</code>, which has
@ -1836,8 +1751,7 @@ a type of `string`, however, `@Input()` properties can have any type, such as
Next, in the child component template, add the following:
<code-example path="inputs-outputs/src/app/item-detail/item-detail.component.html" region="property-in-template" header="src/app/item-detail/item-detail.component.html" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/item-detail/item-detail.component.html" region="property-in-template" header="src/app/item-detail/item-detail.component.html"></code-example>
@ -1850,13 +1764,11 @@ First, use the child's selector, here `<app-item-detail>`, as a directive within
parent component template. Then, use [property binding](guide/template-syntax#property-binding)
to bind the property in the child to the property of the parent.
<code-example path="inputs-outputs/src/app/app.component.html" region="input-parent" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/app.component.html" region="input-parent" header="src/app/app.component.html"></code-example>
Next, in the parent component class, `app.component.ts`, designate a value for `currentItem`:
<code-example path="inputs-outputs/src/app/app.component.ts" region="parent-property" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/app.component.ts" region="parent-property" header="src/app/app.component.ts"></code-example>
With `@Input()`, Angular passes the value for `currentItem` to the child so that `item` renders as `Television`.
@ -1946,8 +1858,7 @@ The following example `@Output()` is called `newItemEvent` and its type is
`EventEmitter`, which means it's an event.
<code-example path="inputs-outputs/src/app/item-output/item-output.component.ts" region="item-output" header="src/app/item-output/item-output.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/item-output/item-output.component.ts" region="item-output" header="src/app/item-output/item-output.component.ts"></code-example>
The different parts of the above declaration are as follows:
@ -1958,8 +1869,7 @@ The different parts of the above declaration are as follows:
Next, create an `addNewItem()` method in the same component class:
<code-example path="inputs-outputs/src/app/item-output/item-output.component.ts" region="item-output-class" header="src/app/item-output/item-output.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/item-output/item-output.component.ts" region="item-output-class" header="src/app/item-output/item-output.component.ts"></code-example>
The `addNewItem()` function uses the `@Output()`, `newItemEvent`,
to raise an event in which it emits the value the user
@ -1974,8 +1884,7 @@ The child's template has two controls. The first is an HTML `<input>` with a
where the user types in an item name. Whatever the user types
into the `<input>` gets stored in the `#newItem` variable.
<code-example path="inputs-outputs/src/app/item-output/item-output.component.html" region="child-output" header="src/app/item-output/item-output.component.html" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/item-output/item-output.component.html" region="child-output" header="src/app/item-output/item-output.component.html"></code-example>
The second element is a `<button>`
with an [event binding](guide/template-syntax#event-binding). You know it's
@ -1997,8 +1906,7 @@ any component in which you could nest the child.
The `AppComponent` in this example features a list of `items`
in an array and a method for adding more items to the array.
<code-example path="inputs-outputs/src/app/app.component.ts" region="add-new-item" header="src/app/app.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/app.component.ts" region="add-new-item" header="src/app/app.component.ts"></code-example>
The `addItem()` method takes an argument in the form of a string
and then pushes, or adds, that string to the `items` array.
@ -2010,8 +1918,7 @@ method to the child's event. Put the child selector, here `<app-item-output>`,
within the parent component's
template, `app.component.html`.
<code-example path="inputs-outputs/src/app/app.component.html" region="output-parent" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/app.component.html" region="output-parent" header="src/app/app.component.html"></code-example>
The event binding, `(newItemEvent)='addItem($event)'`, tells
Angular to connect the event in the child, `newItemEvent`, to
@ -2036,8 +1943,7 @@ The `*ngFor` iterates over the items in the `items` array. When you enter a valu
You can use `@Input()` and `@Output()` on the same child component as in the following:
<code-example path="inputs-outputs/src/app/app.component.html" region="together" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/app.component.html" region="together" header="src/app/app.component.html"></code-example>
The target, `item`, which is an `@Input()` property in the child component class, receives its value from the parent's property, `currentItem`. When you click delete, the child component raises an event, `deleteRequest`, which is the argument for the parent's `crossOffItem()` method.
@ -2063,15 +1969,13 @@ to declare inputs and outputs, you can identify
members in the `inputs` and `outputs` arrays
of the directive metadata, as in this example:
<code-example path="inputs-outputs/src/app/in-the-metadata/in-the-metadata.component.ts" region="metadata" header="src/app/in-the-metadata/in-the-metadata.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/in-the-metadata/in-the-metadata.component.ts" region="metadata" header="src/app/in-the-metadata/in-the-metadata.component.ts"></code-example>
While declaring `inputs` and `outputs` in the `@Directive` and `@Component`
metadata is possible, it is a better practice to use the `@Input()` and `@Output()`
class decorators instead, as follows:
<code-example path="inputs-outputs/src/app/input-output/input-output.component.ts" region="input-output" header="src/app/input-output/input-output.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/input-output/input-output.component.ts" region="input-output" header="src/app/input-output/input-output.component.ts"></code-example>
See the [Decorate input and output properties](guide/styleguide#decorate-input-and-output-properties) section of the
[Style Guide](guide/styleguide) for details.
@ -2104,16 +2008,14 @@ offer a solution.
Alias inputs and outputs in the metadata using a colon-delimited (`:`) string with
the directive property name on the left and the public alias on the right:
<code-example path="inputs-outputs/src/app/aliasing/aliasing.component.ts" region="alias" header="src/app/aliasing/aliasing.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/aliasing/aliasing.component.ts" region="alias" header="src/app/aliasing/aliasing.component.ts"></code-example>
### Aliasing with the `@Input()`/`@Output()` decorator
You can specify the alias for the property name by passing the alias name to the `@Input()`/`@Output()` decorator. The internal name remains as usual.
<code-example path="inputs-outputs/src/app/aliasing/aliasing.component.ts" region="alias-input-output" header="src/app/aliasing/aliasing.component.ts" linenums="false">
</code-example>
<code-example path="inputs-outputs/src/app/aliasing/aliasing.component.ts" region="alias-input-output" header="src/app/aliasing/aliasing.component.ts"></code-example>
<hr/>
@ -2139,25 +2041,21 @@ For example, you might display a number as a currency, change text to uppercase,
Pipes are simple functions that accept an input value and return a transformed value.
They're easy to apply within template expressions, using the pipe operator (`|`):
<code-example path="template-expression-operators/src/app/app.component.html" region="uppercase-pipe" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="uppercase-pipe" header="src/app/app.component.html"></code-example>
The pipe operator passes the result of an expression on the left to a pipe function on the right.
You can chain expressions through multiple pipes:
<code-example path="template-expression-operators/src/app/app.component.html" region="pipe-chain" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="pipe-chain" header="src/app/app.component.html"></code-example>
And you can also [apply parameters](guide/pipes#parameterizing-a-pipe) to a pipe:
<code-example path="template-expression-operators/src/app/app.component.html" region="date-pipe" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="date-pipe" header="src/app/app.component.html"></code-example>
The `json` pipe is particularly helpful for debugging bindings:
<code-example path="template-expression-operators/src/app/app.component.html" region="json-pipe" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="json-pipe" header="src/app/app.component.html"></code-example>
The generated output would look something like this:
@ -2187,8 +2085,7 @@ A good practice is to use parentheses in the third operand too.
The Angular safe navigation operator, `?`, guards against `null` and `undefined`
values in property paths. Here, it protects against a view render failure if `item` is `null`.
<code-example path="template-expression-operators/src/app/app.component.html" region="safe" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="safe" header="src/app/app.component.html"></code-example>
If `item` is `null`, the view still renders but the displayed value is blank; you see only "The item name is:" with nothing after it.
@ -2231,8 +2128,7 @@ an Angular template. For example, after you use [*ngIf](guide/template-syntax#ng
to check that `item` is defined, you can assert that
`item` properties are also defined.
<code-example path="template-expression-operators/src/app/app.component.html" region="non-null" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="template-expression-operators/src/app/app.component.html" region="non-null" header="src/app/app.component.html"></code-example>
When the Angular compiler turns your template into TypeScript code,
it prevents TypeScript from reporting that `item` might be `null` or `undefined`.
@ -2259,8 +2155,7 @@ Sometimes a binding expression triggers a type error during [AOT compilation](gu
to fully specify the type. To silence the error, you can use the `$any()` cast function to cast
the expression to [the `any` type](http://www.typescriptlang.org/docs/handbook/basic-types.html#any) as in the following example:
<code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html"></code-example>
When the Angular compiler turns this template into TypeScript code,
it prevents TypeScript from reporting that `bestByDate` is not a member of the `item`
@ -2269,8 +2164,7 @@ object when it runs type checking on the template.
The `$any()` cast function also works with `this` to allow access to undeclared members of
the component.
<code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-2" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-2" header="src/app/app.component.html"></code-example>
The `$any()` cast function works anywhere in a binding expression where a method call is valid.
@ -2287,13 +2181,11 @@ templates. This means that you will be able to dynamically generate interactive
Refer to the sample code snippet below for a syntax example:
<code-example path="template-syntax/src/app/svg.component.ts" header="src/app/svg.component.ts">
</code-example>
<code-example path="template-syntax/src/app/svg.component.ts" header="src/app/svg.component.ts"></code-example>
Add the below code to your `svg.component.svg` file:
<code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg">
</code-example>
<code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg"></code-example>
Here you can see the use of a `click()` event binding and the property binding syntax
(`[attr.fill]="fillColor"`).

View File

@ -93,12 +93,12 @@ Adopt these two conventions in your own projects for _every kind_ of test file.
## Set up continuous integration
One of the best ways to keep your project bug free is through a test suite, but it's easy to forget to run tests all the time.
One of the best ways to keep your project bug free is through a test suite, but it's easy to forget to run tests all the time.
Continuous integration (CI) servers let you set up your project repository so that your tests run on every commit and pull request.
There are paid CI services like Circle CI and Travis CI, and you can also host your own for free using Jenkins and others.
Although Circle CI and Travis CI are paid services, they are provided free for open source projects.
You can create a public project on GitHub and add these services without paying.
There are paid CI services like Circle CI and Travis CI, and you can also host your own for free using Jenkins and others.
Although Circle CI and Travis CI are paid services, they are provided free for open source projects.
You can create a public project on GitHub and add these services without paying.
Contributions to the Angular repo are automatically run through a whole suite of Circle CI tests.
This article explains how to configure your project to run Circle CI and Travis CI, and also update your test configuration to be able to run tests in the Chrome browser in either environment.
@ -130,12 +130,12 @@ jobs:
- run: npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js
```
This configuration caches `node_modules/` and uses [`npm run`](https://docs.npmjs.com/cli/run-script) to run CLI commands, because `@angular/cli` is not installed globally.
This configuration caches `node_modules/` and uses [`npm run`](https://docs.npmjs.com/cli/run-script) to run CLI commands, because `@angular/cli` is not installed globally.
The double dash (`--`) is needed to pass arguments into the `npm` script.
Step 3: Commit your changes and push them to your repository.
Step 4: [Sign up for Circle CI](https://circleci.com/docs/2.0/first-steps/) and [add your project](https://circleci.com/add-projects).
Step 4: [Sign up for Circle CI](https://circleci.com/docs/2.0/first-steps/) and [add your project](https://circleci.com/add-projects).
Your project should start building.
* Learn more about Circle CI from [Circle CI documentation](https://circleci.com/docs/2.0/).
@ -151,7 +151,7 @@ sudo: false
language: node_js
node_js:
- "10"
addons:
apt:
sources:
@ -175,7 +175,7 @@ This does the same things as the Circle CI configuration, except that Travis doe
Step 2: Commit your changes and push them to your repository.
Step 3: [Sign up for Travis CI](https://travis-ci.org/auth) and [add your project](https://travis-ci.org/profile).
Step 3: [Sign up for Travis CI](https://travis-ci.org/auth) and [add your project](https://travis-ci.org/profile).
You'll need to push a new commit to trigger a build.
* Learn more about Travis CI testing from [Travis CI documentation](https://docs.travis-ci.com/).
@ -184,8 +184,8 @@ You'll need to push a new commit to trigger a build.
When the CLI commands `ng test` and `ng e2e` are generally running the CI tests in your environment, you might still need to adjust your configuration to run the Chrome browser tests.
There are configuration files for both the [Karma JavaScript test runner](https://karma-runner.github.io/latest/config/configuration-file.html)
and [Protractor](https://www.protractortest.org/#/api-overview) end-to-end testing tool,
There are configuration files for both the [Karma JavaScript test runner](https://karma-runner.github.io/latest/config/configuration-file.html)
and [Protractor](https://www.protractortest.org/#/api-overview) end-to-end testing tool,
which you must adjust to start Chrome without sandboxing.
We'll be using [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome#cli) in these examples.
@ -232,7 +232,7 @@ Now you can run the following commands to use the `--no-sandbox` flag:
## Enable code coverage reports
The CLI can run unit tests and create code coverage reports.
The CLI can run unit tests and create code coverage reports.
Code coverage reports show you any parts of our code base that may not be properly tested by your unit tests.
To generate a coverage report run the following command in the root of your project.
@ -255,10 +255,10 @@ If you want to create code-coverage reports every time you test, you can set the
### Code coverage enforcement
The code coverage percentages let you estimate how much of your code is tested.
If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI.
The code coverage percentages let you estimate how much of your code is tested.
If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI.
For example, suppose you want the code base to have a minimum of 80% code coverage.
For example, suppose you want the code base to have a minimum of 80% code coverage.
To enable this, open the [Karma](https://karma-runner.github.io) test platform configuration file, `karma.conf.js`, and add the following in the `coverageIstanbulReporter:` key.
```
@ -294,7 +294,7 @@ calling the service's constructor.
The `MasterService` is a simple example:
<code-example path="testing/src/app/demo/demo.ts" region="MasterService" header="app/demo/demo.ts" linenums="false"></code-example>
<code-example path="testing/src/app/demo/demo.ts" region="MasterService" header="app/demo/demo.ts"></code-example>
`MasterService` delegates its only method, `getValue`, to the injected `ValueService`.
@ -375,8 +375,7 @@ In the following example, the mock is a spy object.
<code-example
path="testing/src/app/demo/demo.testbed.spec.ts"
region="master-service-before-each" linenums="false">
</code-example>
region="master-service-before-each"></code-example>
The test consumes that spy in the same way it did earlier.
@ -400,8 +399,7 @@ Begin by putting re-usable, preparatory code in a _setup_ function instead of `b
<code-example
path="testing/src/app/demo/demo.spec.ts"
region="no-before-each-setup"
header="app/demo/demo.spec.ts (setup)" linenums="false">
</code-example>
header="app/demo/demo.spec.ts (setup)"></code-example>
The `setup()` function returns an object literal
with the variables, such as `masterService`, that a test might reference.
@ -413,8 +411,7 @@ with steps that manipulate the test subject and assert expectations.
<code-example
path="testing/src/app/demo/demo.spec.ts"
region="no-before-each-test" linenums="false">
</code-example>
region="no-before-each-test"></code-example>
Notice how the test uses
[_destructuring assignment_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
@ -497,8 +494,7 @@ Consider this `LightswitchComponent` which toggles a light on and off
<code-example
path="testing/src/app/demo/demo.ts"
region="LightswitchComp"
header="app/demo/demo.ts (LightswitchComp)" linenums="false">
</code-example>
header="app/demo/demo.ts (LightswitchComp)"></code-example>
You might decide only to test that the `clicked()` method
toggles the light's _on/off_ state and sets the message appropriately.
@ -511,16 +507,14 @@ Do the same with the component class.
<code-example
path="testing/src/app/demo/demo.spec.ts"
region="Lightswitch"
header="app/demo/demo.spec.ts (Lightswitch tests)" linenums="false">
</code-example>
header="app/demo/demo.spec.ts (Lightswitch tests)"></code-example>
Here is the `DashboardHeroComponent` from the _Tour of Heroes_ tutorial.
<code-example
path="testing/src/app/dashboard/dashboard-hero.component.ts"
region="class"
header="app/dashboard/dashboard-hero.component.ts (component)" linenums="false">
</code-example>
header="app/dashboard/dashboard-hero.component.ts (component)"></code-example>
It appears within the template of a parent component,
which binds a _hero_ to the `@Input` property and
@ -532,8 +526,7 @@ or its parent component.
<code-example
path="testing/src/app/dashboard/dashboard-hero.component.spec.ts"
region="class-only"
header="app/dashboard/dashboard-hero.component.spec.ts (class tests)" linenums="false">
</code-example>
header="app/dashboard/dashboard-hero.component.spec.ts (class tests)"></code-example>
When a component has dependencies, you may wish to use the `TestBed` to both
create the component and its dependencies.
@ -543,32 +536,28 @@ The following `WelcomeComponent` depends on the `UserService` to know the name o
<code-example
path="testing/src/app/welcome/welcome.component.ts"
region="class"
header="app/welcome/welcome.component.ts" linenums="false">
</code-example>
header="app/welcome/welcome.component.ts"></code-example>
You might start by creating a mock of the `UserService` that meets the minimum needs of this component.
<code-example
path="testing/src/app/welcome/welcome.component.spec.ts"
region="mock-user-service"
header="app/welcome/welcome.component.spec.ts (MockUserService)" linenums="false">
</code-example>
header="app/welcome/welcome.component.spec.ts (MockUserService)"></code-example>
Then provide and inject _both the_ **component** _and the service_ in the `TestBed` configuration.
<code-example
path="testing/src/app/welcome/welcome.component.spec.ts"
region="class-only-before-each"
header="app/welcome/welcome.component.spec.ts (class-only setup)" linenums="false">
</code-example>
header="app/welcome/welcome.component.spec.ts (class-only setup)"></code-example>
Then exercise the component class, remembering to call the [lifecycle hook methods](guide/lifecycle-hooks) as Angular does when running the app.
<code-example
path="testing/src/app/welcome/welcome.component.spec.ts"
region="class-only-tests"
header="app/welcome/welcome.component.spec.ts (class-only tests)" linenums="false">
</code-example>
header="app/welcome/welcome.component.spec.ts (class-only tests)"></code-example>
### Component DOM testing
@ -619,8 +608,7 @@ It also generates an initial test file for the component, `banner-external.compo
<code-example
path="testing/src/app/banner/banner-initial.component.spec.ts"
region="v1"
header="app/banner/banner-external.component.spec.ts (initial)" linenums="false">
</code-example>
header="app/banner/banner-external.component.spec.ts (initial)"></code-example>
#### Reduce the setup
@ -635,8 +623,7 @@ For now, you can radically reduce this test file to a more manageable size:
<code-example
path="testing/src/app/banner/banner-initial.component.spec.ts"
region="v2"
header="app/banner/banner-initial.component.spec.ts (minimal)" linenums="false">
</code-example>
header="app/banner/banner-initial.component.spec.ts (minimal)"></code-example>
In this example, the metadata object passed to `TestBed.configureTestingModule`
simply declares `BannerComponent`, the component to test.
@ -708,8 +695,7 @@ you refactor to pull the setup into a Jasmine `beforeEach()` and some supporting
<code-example
path="testing/src/app/banner/banner-initial.component.spec.ts"
region="v3"
linenums="false">
</code-example>
></code-example>
Now add a test that gets the component's element from `fixture.nativeElement` and
looks for the expected text.
@ -853,8 +839,7 @@ the component's `title` property like this.
<code-example
path="testing/src/app/banner/banner.component.ts"
region="component"
header="app/banner/banner.component.ts" linenums="false">
</code-example>
header="app/banner/banner.component.ts"></code-example>
Simple as this is, you decide to add a test to confirm that component
actually displays the right content where you think it should.
@ -870,8 +855,7 @@ and assign it to the `h1` variable.
<code-example
path="testing/src/app/banner/banner.component.spec.ts"
region="setup"
header="app/banner/banner.component.spec.ts (setup)" linenums="false">
</code-example>
header="app/banner/banner.component.spec.ts (setup)"></code-example>
{@a detect-changes}
@ -901,8 +885,7 @@ The `TestBed.createComponent` does _not_ trigger change detection.
a fact confirmed in the revised test:
<code-example
path="testing/src/app/banner/banner.component.spec.ts" region="test-w-o-detect-changes" linenums="false">
</code-example>
path="testing/src/app/banner/banner.component.spec.ts" region="test-w-o-detect-changes"></code-example>
#### _detectChanges()_
@ -935,15 +918,15 @@ Some testers prefer that the Angular test environment run change detection autom
That's possible by configuring the `TestBed` with the `ComponentFixtureAutoDetect` provider.
First import it from the testing utility library:
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="import-ComponentFixtureAutoDetect" header="app/banner/banner.component.detect-changes.spec.ts (import)" linenums="false"></code-example>
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="import-ComponentFixtureAutoDetect" header="app/banner/banner.component.detect-changes.spec.ts (import)"></code-example>
Then add it to the `providers` array of the testing module configuration:
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="auto-detect" header="app/banner/banner.component.detect-changes.spec.ts (AutoDetect)" linenums="false"></code-example>
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="auto-detect" header="app/banner/banner.component.detect-changes.spec.ts (AutoDetect)"></code-example>
Here are three tests that illustrate how automatic change detection works.
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="auto-detect-tests" header="app/banner/banner.component.detect-changes.spec.ts (AutoDetect Tests)" linenums="false"></code-example>
<code-example path="testing/src/app/banner/banner.component.detect-changes.spec.ts" region="auto-detect-tests" header="app/banner/banner.component.detect-changes.spec.ts (AutoDetect Tests)"></code-example>
The first test shows the benefit of automatic change detection.
@ -993,8 +976,7 @@ as the following variant of `BannerComponent` does.
<code-example
path="testing/src/app/banner/banner-external.component.ts"
region="metadata"
header="app/banner/banner-external.component.ts (metadata)" linenums="false">
</code-example>
header="app/banner/banner-external.component.ts (metadata)"></code-example>
This syntax tells the Angular compiler to read the external files during component compilation.
@ -1025,12 +1007,12 @@ Components often have service dependencies.
The `WelcomeComponent` displays a welcome message to the logged in user.
It knows who the user is based on a property of the injected `UserService`:
<code-example path="testing/src/app/welcome/welcome.component.ts" header="app/welcome/welcome.component.ts" linenums="false"></code-example>
<code-example path="testing/src/app/welcome/welcome.component.ts" header="app/welcome/welcome.component.ts"></code-example>
The `WelcomeComponent` has decision logic that interacts with the service, logic that makes this component worth testing.
Here's the testing module configuration for the spec file, `app/welcome/welcome.component.spec.ts`:
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="config-test-module" header="app/welcome/welcome.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="config-test-module" header="app/welcome/welcome.component.spec.ts"></code-example>
This time, in addition to declaring the _component-under-test_,
the configuration adds a `UserService` provider to the `providers` list.
@ -1057,8 +1039,7 @@ and its tests:
<code-example
path="testing/src/app/welcome/welcome.component.spec.ts"
region="user-service-stub"
header="app/welcome/welcome.component.spec.ts" linenums="false">
</code-example>
header="app/welcome/welcome.component.spec.ts"></code-example>
{@a get-injected-service}
@ -1115,7 +1096,7 @@ that's provided to the testing module in the body of your test.
The `userService` instance injected into the component is a completely _different_ object,
a clone of the provided `userServiceStub`.
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="stub-not-injected" header="app/welcome/welcome.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="stub-not-injected" header="app/welcome/welcome.component.spec.ts"></code-example>
{@a welcome-spec-setup}
@ -1123,11 +1104,11 @@ a clone of the provided `userServiceStub`.
Here's the complete `beforeEach()`, using `TestBed.get()`:
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="setup" header="app/welcome/welcome.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="setup" header="app/welcome/welcome.component.spec.ts"></code-example>
And here are some tests:
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="tests" header="app/welcome/welcome.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/welcome/welcome.component.spec.ts" region="tests" header="app/welcome/welcome.component.spec.ts"></code-example>
The first is a sanity test; it confirms that the stubbed `UserService` is called and working.
@ -1155,8 +1136,7 @@ The `TwainComponent` displays Mark Twain quotes.
<code-example
path="testing/src/app/twain/twain.component.ts"
region="template"
header="app/twain/twain.component.ts (template)" linenums="false">
</code-example>
header="app/twain/twain.component.ts (template)"></code-example>
Note that value of the component's `quote` property passes through an `AsyncPipe`.
That means the property returns either a `Promise` or an `Observable`.
@ -1167,8 +1147,7 @@ the `quote` property returns an `Observable`.
<code-example
path="testing/src/app/twain/twain.component.ts"
region="get-quote"
header="app/twain/twain.component.ts (getQuote)" linenums="false">
</code-example>
header="app/twain/twain.component.ts (getQuote)"></code-example>
The `TwainComponent` gets quotes from an injected `TwainService`.
The component starts the returned `Observable` with a placeholder value (`'...'`),
@ -1190,8 +1169,7 @@ They should emulate such calls. The setup in this `app/twain/twain.component.spe
<code-example
path="testing/src/app/twain/twain.component.spec.ts"
region="setup"
header="app/twain/twain.component.spec.ts (setup)" linenums="false">
</code-example>
header="app/twain/twain.component.spec.ts (setup)"></code-example>
{@a service-spy}
@ -1323,11 +1301,11 @@ If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macr
<code-tabs>
<code-pane
path="testing/src/app/shared/canvas.component.spec.ts"
header="src/app/shared/canvas.component.spec.ts" linenums="false">
header="src/app/shared/canvas.component.spec.ts">
</code-pane>
<code-pane
path="testing/src/app/shared/canvas.component.ts"
header="src/app/shared/canvas.component.ts" linenums="false">
header="src/app/shared/canvas.component.ts">
</code-pane>
</code-tabs>
@ -1497,8 +1475,7 @@ The first one subscribes to the `Observable` exposed to the template by the comp
<code-example
path="testing/src/app/twain/twain.component.spec.ts"
region="quote-done-test" linenums="false">
</code-example>
region="quote-done-test"></code-example>
The RxJS `last()` operator emits the observable's last value before completing, which will be the test quote.
The `subscribe` callback calls `detectChanges()` to
@ -1512,8 +1489,7 @@ can give you that information and make assertions about the state of the view.
<code-example
path="testing/src/app/twain/twain.component.spec.ts"
region="spy-done-test" linenums="false">
</code-example>
region="spy-done-test"></code-example>
<hr>
@ -1545,15 +1521,13 @@ Then import the symbols you need.
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="import-marbles"
header="app/twain/twain.component.marbles.spec.ts (import marbles)" linenums="false">
</code-example>
header="app/twain/twain.component.marbles.spec.ts (import marbles)"></code-example>
Here's the complete test for getting a quote:
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="get-quote-test" linenums="false">
</code-example>
region="get-quote-test"></code-example>
Notice that the Jasmine test is synchronous. There's no `fakeAsync()`.
Marble testing uses a test scheduler to simulate the passage of time
@ -1567,8 +1541,7 @@ In the second argument you map the value marker (`x`) to the emitted value (`tes
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="test-quote-marbles" linenums="false">
</code-example>
region="test-quote-marbles"></code-example>
The marble library constructs the corresponding observable, which the
test sets as the `getQuote` spy's return value.
@ -1578,8 +1551,7 @@ you tell the `TestScheduler` to _flush_ its queue of prepared tasks like this.
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="test-scheduler-flush" linenums="false">
</code-example>
region="test-scheduler-flush"></code-example>
This step serves a purpose analogous to `tick()` and `whenStable()` in the
earlier `fakeAsync()` and `async()` examples.
@ -1591,8 +1563,7 @@ Here's the marble testing version of the `getQuote()` error test.
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="error-test" linenums="false">
</code-example>
region="error-test"></code-example>
It's still an async test, calling `fakeAsync()` and `tick()`, because the component itself
calls `setTimeout()` when processing errors.
@ -1601,8 +1572,7 @@ Look at the marble observable definition.
<code-example
path="testing/src/app/twain/twain.component.marbles.spec.ts"
region="error-marbles" linenums="false">
</code-example>
region="error-marbles"></code-example>
This is a _cold_ observable that waits three frames and then emits an error,
The hash (`#`) indicates the timing of the error that is specified in the third argument.
@ -1649,8 +1619,7 @@ The `DashboardHeroComponent` is embedded in the `DashboardComponent` template li
<code-example
path="testing/src/app/dashboard/dashboard.component.html"
region="dashboard-hero"
header="app/dashboard/dashboard.component.html (excerpt)" linenums="false">
</code-example>
header="app/dashboard/dashboard.component.html (excerpt)"></code-example>
The `DashboardHeroComponent` appears in an `*ngFor` repeater, which sets each component's `hero` input property
to the looping value and listens for the component's `selected` event.
@ -1662,8 +1631,7 @@ Here's the component's full definition:
<code-example
path="testing/src/app/dashboard/dashboard-hero.component.ts"
region="component"
header="app/dashboard/dashboard-hero.component.ts (component)" linenums="false">
</code-example>
header="app/dashboard/dashboard-hero.component.ts (component)"></code-example>
While testing a component this simple has little intrinsic value, it's worth knowing how.
You can use one of these approaches:
@ -1677,8 +1645,7 @@ A quick look at the `DashboardComponent` constructor discourages the first appro
<code-example
path="testing/src/app/dashboard/dashboard.component.ts"
region="ctor"
header="app/dashboard/dashboard.component.ts (constructor)" linenums="false">
</code-example>
header="app/dashboard/dashboard.component.ts (constructor)"></code-example>
The `DashboardComponent` depends on the Angular router and the `HeroService`.
You'd probably have to replace them both with test doubles, which is a lot of work.
@ -1702,8 +1669,7 @@ Here's the meat of the spec file setup.
<code-example
path="testing/src/app/dashboard/dashboard-hero.component.spec.ts"
region="setup"
header="app/dashboard/dashboard-hero.component.spec.ts (setup)" linenums="false">
</code-example>
header="app/dashboard/dashboard-hero.component.spec.ts (setup)"></code-example>
Note how the setup code assigns a test hero (`expectedHero`) to the component's `hero` property,
emulating the way the `DashboardComponent` would set it
@ -1801,8 +1767,7 @@ in a helper such as the `click()` function below:
<code-example
path="testing/src/testing/index.ts"
region="click-event"
header="testing/index.ts (click helper)" linenums="false">
</code-example>
header="testing/index.ts (click helper)"></code-example>
The first parameter is the _element-to-click_. If you wish, you can pass a
custom event object as the second parameter. The default is a (partial)
@ -1849,8 +1814,7 @@ that can be made satisfactorily with a _test host_ like this one:
path="testing/src/app/dashboard/dashboard-hero.component.spec.ts"
region="test-host"
header="app/dashboard/dashboard-hero.component.spec.ts (test host)"
linenums="false">
</code-example>
></code-example>
This test host binds to `DashboardHeroComponent` as the `DashboardComponent` would
but without the noise of the `Router`, the `HeroService`, or the `*ngFor` repeater.
@ -1864,7 +1828,7 @@ Later, the tests will be able to easily check `selectedHero` to verify that the
The setup for the _test-host_ tests is similar to the setup for the stand-alone tests:
<code-example path="testing/src/app/dashboard/dashboard-hero.component.spec.ts" region="test-host-setup" header="app/dashboard/dashboard-hero.component.spec.ts (test host setup)" linenums="false"></code-example>
<code-example path="testing/src/app/dashboard/dashboard-hero.component.spec.ts" region="test-host-setup" header="app/dashboard/dashboard-hero.component.spec.ts (test host setup)"></code-example>
This testing module configuration shows three important differences:
@ -1884,8 +1848,7 @@ The tests themselves are almost identical to the stand-alone version:
<code-example
path="testing/src/app/dashboard/dashboard-hero.component.spec.ts"
region="test-host-tests"
header="app/dashboard/dashboard-hero.component.spec.ts (test-host)" linenums="false">
</code-example>
header="app/dashboard/dashboard-hero.component.spec.ts (test-host)"></code-example>
Only the selected event test differs. It confirms that the selected `DashboardHeroComponent` hero
really does find its way up through the event binding to the host component.
@ -1907,8 +1870,7 @@ which it injects together with the `HeroService`.
<code-example
path="testing/src/app/dashboard/dashboard.component.ts"
region="ctor"
header="app/dashboard/dashboard.component.ts (constructor)" linenums="false">
</code-example>
header="app/dashboard/dashboard.component.ts (constructor)"></code-example>
Mocking the `HeroService` with a spy is a [familiar story](#component-with-async-service).
But the `Router` has a complicated API and is entwined with other services and application preconditions. Might it be difficult to mock?
@ -1931,8 +1893,7 @@ as providing a `HeroService` spy.
<code-example
path="testing/src/app/dashboard/dashboard.component.spec.ts"
region="router-spy"
header="app/dashboard/dashboard.component.spec.ts (spies)" linenums="false">
</code-example>
header="app/dashboard/dashboard.component.spec.ts (spies)"></code-example>
The following test clicks the displayed hero and confirms that
`Router.navigateByUrl` is called with the expected url.
@ -1940,8 +1901,7 @@ The following test clicks the displayed hero and confirms that
<code-example
path="testing/src/app/dashboard/dashboard.component.spec.ts"
region="navigate-test"
header="app/dashboard/dashboard.component.spec.ts (navigate test)" linenums="false">
</code-example>
header="app/dashboard/dashboard.component.spec.ts (navigate test)"></code-example>
{@a routed-component-w-param}
@ -1961,7 +1921,7 @@ injects it into a new instance of the `HeroDetailComponent`.
Here's the `HeroDetailComponent` constructor:
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="ctor" header="app/hero/hero-detail.component.ts (constructor)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="ctor" header="app/hero/hero-detail.component.ts (constructor)"></code-example>
The `HeroDetail` component needs the `id` parameter so it can fetch
the corresponding hero via the `HeroDetailService`.
@ -1972,7 +1932,7 @@ It can't just reference the `id` property of the `ActivatedRoute.paramMap`.
The component has to _subscribe_ to the `ActivatedRoute.paramMap` observable and be prepared
for the `id` to change during its lifetime.
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="ng-on-init" header="app/hero/hero-detail.component.ts (ngOnInit)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="ng-on-init" header="app/hero/hero-detail.component.ts (ngOnInit)"></code-example>
<div class="alert is-helpful">
@ -2001,8 +1961,7 @@ The following `ActivatedRouteStub` class serves as a test double for `ActivatedR
<code-example
path="testing/src/testing/activated-route-stub.ts"
region="activated-route-stub"
header="testing/activated-route-stub.ts (ActivatedRouteStub)" linenums="false">
</code-example>
header="testing/activated-route-stub.ts (ActivatedRouteStub)"></code-example>
Consider placing such helpers in a `testing` folder sibling to the `app` folder.
This sample puts `ActivatedRouteStub` in `testing/activated-route-stub.ts`.
@ -2020,7 +1979,7 @@ the [_marble testing library_](#marble-testing).
Here's a test demonstrating the component's behavior when the observed `id` refers to an existing hero:
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="route-good-id" header="app/hero/hero-detail.component.spec.ts (existing id)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="route-good-id" header="app/hero/hero-detail.component.spec.ts (existing id)"></code-example>
<div class="alert is-helpful">
@ -2035,7 +1994,7 @@ The test suite setup provided the same router spy [described above](#routing-com
This test expects the component to try to navigate to the `HeroListComponent`.
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="route-bad-id" header="app/hero/hero-detail.component.spec.ts (bad id)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="route-bad-id" header="app/hero/hero-detail.component.spec.ts (bad id)"></code-example>
While this app doesn't have a route to the `HeroDetailComponent` that omits the `id` parameter, it might add such a route someday.
The component should do something reasonable when there is no `id`.
@ -2046,8 +2005,7 @@ New heroes have `id=0` and a blank `name`. This test confirms that the component
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="route-no-id"
header="app/hero/hero-detail.component.spec.ts (no id)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (no id)"></code-example>
<hr>
@ -2063,8 +2021,7 @@ The `AppComponent`, for example, displays a navigation bar with anchors and thei
<code-example
path="testing/src/app/app.component.html"
header="app/app.component.html" linenums="false">
</code-example>
header="app/app.component.html"></code-example>
While the `AppComponent` _class_ is empty,
you may want to write unit tests to confirm that the links are wired properly
@ -2102,8 +2059,7 @@ and directive that play little or no role in the tests.
<code-example
path="testing/src/app/app.component.spec.ts"
region="component-stubs"
header="app/app.component.spec.ts (stub declaration)" linenums="false">
</code-example>
header="app/app.component.spec.ts (stub declaration)"></code-example>
The stub selectors match the selectors for the corresponding real components.
But their templates and classes are empty.
@ -2114,8 +2070,7 @@ components, directives, and pipes that need to be real.
<code-example
path="testing/src/app/app.component.spec.ts"
region="testbed-stubs"
header="app/app.component.spec.ts (TestBed stubs)" linenums="false">
</code-example>
header="app/app.component.spec.ts (TestBed stubs)"></code-example>
The `AppComponent` is the test subject, so of course you declare the real version.
@ -2133,8 +2088,7 @@ In the second approach, add `NO_ERRORS_SCHEMA` to the `TestBed.schemas` metadata
<code-example
path="testing/src/app/app.component.spec.ts"
region="no-errors-schema"
header="app/app.component.spec.ts (NO_ERRORS_SCHEMA)" linenums="false">
</code-example>
header="app/app.component.spec.ts (NO_ERRORS_SCHEMA)"></code-example>
The `NO_ERRORS_SCHEMA` tells the Angular compiler to ignore unrecognized elements and attributes.
@ -2170,8 +2124,7 @@ as seen in this example.
<code-example
path="testing/src/app/app.component.spec.ts"
region="mixed-setup"
header="app/app.component.spec.ts (mixed setup)" linenums="false">
</code-example>
header="app/app.component.spec.ts (mixed setup)"></code-example>
The Angular compiler creates the `BannerComponentStub` for the `<app-banner>` element
and applies the `RouterLinkStubDirective` to the anchors with the `routerLink` attribute,
@ -2193,8 +2146,7 @@ seen in the `AppComponent` template.
<code-example
path="testing/src/testing/router-link-directive-stub.ts"
region="router-link"
header="testing/router-link-directive-stub.ts (RouterLinkDirectiveStub)" linenums="false">
</code-example>
header="testing/router-link-directive-stub.ts (RouterLinkDirectiveStub)"></code-example>
The URL bound to the `[routerLink]` attribute flows in to the directive's `linkParams` property.
@ -2223,8 +2175,7 @@ A little more setup triggers the initial data binding and gets references to the
<code-example
path="testing/src/app/app.component.spec.ts"
region="test-setup"
header="app/app.component.spec.ts (test setup)" linenums="false">
</code-example>
header="app/app.component.spec.ts (test setup)"></code-example>
Three points of special interest:
@ -2240,15 +2191,14 @@ The `AppComponent` links to validate are as follows:
<code-example
path="testing/src/app/app.component.html"
region="links"
header="app/app.component.html (navigation links)" linenums="false">
</code-example>
header="app/app.component.html (navigation links)"></code-example>
{@a app-component-tests}
Here are some tests that confirm those links are wired to the `routerLink` directives
as expected:
<code-example path="testing/src/app/app.component.spec.ts" region="tests" header="app/app.component.spec.ts (selected tests)" linenums="false"></code-example>
<code-example path="testing/src/app/app.component.spec.ts" region="tests" header="app/app.component.spec.ts (selected tests)"></code-example>
<div class="alert is-helpful">
@ -2303,8 +2253,7 @@ The `HeroDetailComponent` is a simple view with a title, two hero fields, and tw
But there's plenty of template complexity even in this simple form.
<code-example
path="testing/src/app/hero/hero-detail.component.html" header="app/hero/hero-detail.component.html" linenums="false">
</code-example>
path="testing/src/app/hero/hero-detail.component.html" header="app/hero/hero-detail.component.html"></code-example>
Tests that exercise the component need ...
@ -2324,8 +2273,7 @@ Here is such a `Page` class for the `hero-detail.component.spec.ts`
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="page"
header="app/hero/hero-detail.component.spec.ts (Page)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (Page)"></code-example>
Now the important hooks for component manipulation and inspection are neatly organized and accessible from an instance of `Page`.
@ -2334,8 +2282,7 @@ A `createComponent` method creates a `page` object and fills in the blanks once
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="create-component"
header="app/hero/hero-detail.component.spec.ts (createComponent)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (createComponent)"></code-example>
The [_HeroDetailComponent_ tests](#tests-w-test-double) in an earlier section demonstrate how `createComponent` and `page`
keep the tests short and _on message_.
@ -2346,8 +2293,7 @@ Here are a few more `HeroDetailComponent` tests to reinforce the point.
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="selected-tests"
header="app/hero/hero-detail.component.spec.ts (selected tests)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (selected tests)"></code-example>
<hr>
@ -2374,8 +2320,7 @@ the following version of the `BannerComponent` does.
<code-example
path="testing/src/app/banner/banner-external.component.ts"
header="app/banner/banner-external.component.ts (external template & css)" linenums="false">
</code-example>
header="app/banner/banner-external.component.ts (external template & css)"></code-example>
The test fails when the `TestBed` tries to create the component.
@ -2383,8 +2328,7 @@ The test fails when the `TestBed` tries to create the component.
path="testing/src/app/banner/banner.component.spec.ts"
region="configure-and-create"
header="app/banner/banner.component.spec.ts (setup that fails)"
avoid linenums="false">
</code-example>
avoid></code-example>
Recall that the app hasn't been compiled.
So when you call `createComponent()`, the `TestBed` compiles implicitly.
@ -2434,8 +2378,7 @@ Write the first async `beforeEach` like this.
<code-example
path="testing/src/app/banner/banner-external.component.spec.ts"
region="async-before-each"
header="app/banner/banner-external.component.spec.ts (async beforeEach)" linenums="false">
</code-example>
header="app/banner/banner-external.component.spec.ts (async beforeEach)"></code-example>
The `async()` helper function takes a parameterless function with the body of the setup.
@ -2470,8 +2413,7 @@ which include creating the component and querying for elements to inspect.
<code-example
path="testing/src/app/banner/banner-external.component.spec.ts"
region="sync-before-each"
header="app/banner/banner-external.component.spec.ts (synchronous beforeEach)" linenums="false">
</code-example>
header="app/banner/banner-external.component.spec.ts (synchronous beforeEach)"></code-example>
You can count on the test runner to wait for the first asynchronous `beforeEach` to finish before calling the second.
@ -2486,8 +2428,7 @@ into a `then(...)` callback.
<code-example
path="testing/src/app/banner/banner-external.component.spec.ts"
region="one-before-each"
header="app/banner/banner-external.component.spec.ts (one beforeEach)" linenums="false">
</code-example>
header="app/banner/banner-external.component.spec.ts (one beforeEach)"></code-example>
#### _compileComponents()_ is harmless
@ -2533,8 +2474,7 @@ One approach is to configure the testing module from the individual pieces as in
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="setup-forms-module"
header="app/hero/hero-detail.component.spec.ts (FormsModule setup)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (FormsModule setup)"></code-example>
<div class="alert is-helpful">
@ -2557,8 +2497,7 @@ The test configuration can use the `SharedModule` too as seen in this alternativ
<code-example
path="testing/src/app/hero/hero-detail.component.spec.ts"
region="setup-shared-module"
header="app/hero/hero-detail.component.spec.ts (SharedModule setup)" linenums="false">
</code-example>
header="app/hero/hero-detail.component.spec.ts (SharedModule setup)"></code-example>
It's a bit tighter and smaller, with fewer import statements (not shown).
@ -2570,7 +2509,7 @@ The `HeroDetailComponent` is part of the `HeroModule` [Feature Module](guide/fea
including the `SharedModule`.
Try a test configuration that imports the `HeroModule` like this one:
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="setup-hero-module" header="app/hero/hero-detail.component.spec.ts (HeroModule setup)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="setup-hero-module" header="app/hero/hero-detail.component.spec.ts (HeroModule setup)"></code-example>
That's _really_ crisp. Only the _test doubles_ in the `providers` remain. Even the `HeroDetailComponent` declaration is gone.
@ -2594,7 +2533,7 @@ the module is small, as feature modules tend to be.
The `HeroDetailComponent` provides its own `HeroDetailService`.
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="prototype" header="app/hero/hero-detail.component.ts (prototype)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.ts" region="prototype" header="app/hero/hero-detail.component.ts (prototype)"></code-example>
It's not possible to stub the component's `HeroDetailService` in the `providers` of the `TestBed.configureTestingModule`.
Those are providers for the _testing module_, not the component. They prepare the dependency injector at the _fixture level_.
@ -2614,7 +2553,7 @@ There might not be a remote server to call.
Fortunately, the `HeroDetailService` delegates responsibility for remote data access to an injected `HeroService`.
<code-example path="testing/src/app/hero/hero-detail.service.ts" region="prototype" header="app/hero/hero-detail.service.ts (prototype)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.service.ts" region="prototype" header="app/hero/hero-detail.service.ts (prototype)"></code-example>
The [previous test configuration](#feature-module-import) replaces the real `HeroService` with a `TestHeroService`
that intercepts server requests and fakes their responses.
@ -2627,7 +2566,7 @@ What if `HeroDetailService` makes its own server requests?
The `TestBed.overrideComponent` method can replace the component's `providers` with easy-to-manage _test doubles_
as seen in the following setup variation:
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="setup-override" header="app/hero/hero-detail.component.spec.ts (Override setup)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="setup-override" header="app/hero/hero-detail.component.spec.ts (Override setup)"></code-example>
Notice that `TestBed.configureTestingModule` no longer provides a (fake) `HeroService` because it's [not needed](#spy-stub).
@ -2637,7 +2576,7 @@ Notice that `TestBed.configureTestingModule` no longer provides a (fake) `HeroSe
Focus on the `overrideComponent` method.
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="override-component-method" header="app/hero/hero-detail.component.spec.ts (overrideComponent)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="override-component-method" header="app/hero/hero-detail.component.spec.ts (overrideComponent)"></code-example>
It takes two arguments: the component type to override (`HeroDetailComponent`) and an override metadata object.
The [override metadata object](#metadata-override-object) is a generic defined as follows:
@ -2678,7 +2617,7 @@ The related `HeroDetailComponent` tests will assert that methods of the `HeroDet
were called by spying on the service methods.
Accordingly, the stub implements its methods as spies:
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="hds-spy" header="app/hero/hero-detail.component.spec.ts (HeroDetailServiceSpy)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="hds-spy" header="app/hero/hero-detail.component.spec.ts (HeroDetailServiceSpy)"></code-example>
{@a override-tests}
@ -2687,7 +2626,7 @@ Accordingly, the stub implements its methods as spies:
Now the tests can control the component's hero directly by manipulating the spy-stub's `testHero`
and confirm that service methods were called.
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="override-tests" header="app/hero/hero-detail.component.spec.ts (override tests)" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="override-tests" header="app/hero/hero-detail.component.spec.ts (override tests)"></code-example>
{@a more-overrides}
@ -2713,16 +2652,16 @@ based on either a data bound color or a default color (lightgray).
It also sets a custom property of the element (`customProperty`) to `true`
for no reason other than to show that it can.
<code-example path="testing/src/app/shared/highlight.directive.ts" header="app/shared/highlight.directive.ts" linenums="false"></code-example>
<code-example path="testing/src/app/shared/highlight.directive.ts" header="app/shared/highlight.directive.ts"></code-example>
It's used throughout the application, perhaps most simply in the `AboutComponent`:
<code-example path="testing/src/app/about/about.component.ts" header="app/about/about.component.ts" linenums="false"></code-example>
<code-example path="testing/src/app/about/about.component.ts" header="app/about/about.component.ts"></code-example>
Testing the specific use of the `HighlightDirective` within the `AboutComponent` requires only the
techniques explored above (in particular the ["Shallow test"](#nested-component-tests) approach).
<code-example path="testing/src/app/about/about.component.spec.ts" region="tests" header="app/about/about.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/about/about.component.spec.ts" region="tests" header="app/about/about.component.spec.ts"></code-example>
However, testing a single use case is unlikely to explore the full range of a directive's capabilities.
Finding and testing all components that use the directive is tedious, brittle, and almost as unlikely to afford full coverage.
@ -2734,7 +2673,7 @@ do not inspire confidence in the directive's efficacy.
A better solution is to create an artificial test component that demonstrates all ways to apply the directive.
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="test-component" header="app/shared/highlight.directive.spec.ts (TestComponent)" linenums="false"></code-example>
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="test-component" header="app/shared/highlight.directive.spec.ts (TestComponent)"></code-example>
<figure>
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
@ -2783,7 +2722,7 @@ metadata and an interface.
Consider a `TitleCasePipe` that capitalizes the first letter of each word.
Here's a naive implementation with a regular expression.
<code-example path="testing/src/app/shared/title-case.pipe.ts" header="app/shared/title-case.pipe.ts" linenums="false"></code-example>
<code-example path="testing/src/app/shared/title-case.pipe.ts" header="app/shared/title-case.pipe.ts"></code-example>
Anything that uses a regular expression is worth testing thoroughly.
Use simple Jasmine to explore the expected cases and the edge cases.
@ -3147,7 +3086,7 @@ Here are the most important static methods, in order of likely utility.
the object to return if Angular can't find the provider
(`null` in this example):
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="testbed-get-w-null" header="app/demo/demo.testbed.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="testbed-get-w-null" header="app/demo/demo.testbed.spec.ts"></code-example>
After calling `get`, the `TestBed` configuration is frozen for the duration of the current spec.
@ -3607,7 +3546,7 @@ predicate that filters the source element's subtree for matching `DebugElement`.
The predicate is any method that takes a `DebugElement` and returns a _truthy_ value.
The following example finds all `DebugElements` with a reference to a template local variable named "content":
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="custom-predicate" header="app/demo/demo.testbed.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="custom-predicate" header="app/demo/demo.testbed.spec.ts"></code-example>
The Angular `By` class has three static methods for common predicates:
@ -3615,7 +3554,7 @@ The Angular `By` class has three static methods for common predicates:
- `By.css(selector)` - return elements with matching CSS selectors.
- `By.directive(directive)` - return elements that Angular matched to an instance of the directive class.
<code-example path="testing/src/app/hero/hero-list.component.spec.ts" region="by" header="app/hero/hero-list.component.spec.ts" linenums="false"></code-example>
<code-example path="testing/src/app/hero/hero-list.component.spec.ts" region="by" header="app/hero/hero-list.component.spec.ts"></code-example>
<hr>

View File

@ -22,13 +22,11 @@ Here's another code sample using the wildcard state together with our previous e
This allows us to add new states without having to include separate transitions for each one.
<code-example header="src/app/open-close.component.ts" path="animations/src/app/open-close.component.ts" region="trigger-wildcard1" language="typescript" linenums="false">
</code-example>
<code-example header="src/app/open-close.component.ts" path="animations/src/app/open-close.component.ts" region="trigger-wildcard1" language="typescript"></code-example>
Use a double arrow syntax to specify state-to-state transitions in both directions.
<code-example header="src/app/open-close.component.ts" path="animations/src/app/open-close.component.ts" region="trigger-wildcard2" language="typescript" linenums="false">
</code-example>
<code-example header="src/app/open-close.component.ts" path="animations/src/app/open-close.component.ts" region="trigger-wildcard2" language="typescript"></code-example>
### Using wildcard state with multiple transition states
@ -39,9 +37,7 @@ In our two-state button example, the wildcard isn't that useful because there ar
</figure>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" linenums="false"
region="trigger-transition" language="typescript">
</code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="trigger-transition" language="typescript"></code-example>
The `* => *` transition applies when any change between two states takes place.
@ -54,9 +50,7 @@ To do this, list the more specific transitions *before* `* => *`.
Use the wildcard `*` with a style to tell the animation to use whatever the current style value is, and animate with that. Wildcard is a fallback value that's used if the state being animated isn't declared within the trigger.
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" linenums="false"
region="transition4" language="typescript">
</code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="transition4" language="typescript"></code-example>
### Void state
@ -88,8 +82,7 @@ Now we'll add a new behavior:
* When you add a hero to the list of heroes, it appears to fly onto the page from the left.
* When you remove a hero from the list, it appears to fly out to the right.
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" header="src/app/hero-list-enter-leave.component.ts" region="animationdef" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" header="src/app/hero-list-enter-leave.component.ts" region="animationdef" language="typescript"></code-example>
In the above code, we applied the `void` state when the HTML element isn't attached to a view.
@ -134,7 +127,7 @@ The `transition()` function takes additional selector values, `:increment` and `
</div>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="increment" language="typescript" linenums="false"></code-example>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="increment" language="typescript"></code-example>
## Boolean values in transitions
@ -188,8 +181,7 @@ However, selective child animations can still be run on a disabled parent in one
To disable all animations for an Angular app, place the `@.disabled` host binding on the topmost Angular component.
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="toggle-app-animations" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="toggle-app-animations" language="typescript"></code-example>
<div class="alert is-helpful">
@ -200,8 +192,7 @@ To disable all animations for an Angular app, place the `@.disabled` host bindin
The animation `trigger()` function emits *callbacks* when it starts and when it finishes. In the example below we have a component that contains an `openClose` trigger.
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="events1" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="events1" language="typescript"></code-example>
In the HTML template, the animation event is passed back via `$event`, as `@trigger.start` and `@trigger.done`, where `trigger` is the name of the trigger being used. In our example, the trigger `openClose` appears as follows.
@ -216,8 +207,7 @@ An animation can influence an end user to *perceive* the operation as faster, ev
Callbacks can serve as a debugging tool, for example in conjunction with `console.warn()` to view the application's progress in a browser's Developer JavaScript Console. The following code snippet creates console log output for our original example, a button with the two states of `open` and `closed`.
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="events" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" region="events" language="typescript"></code-example>
{@a keyframes}
@ -233,8 +223,7 @@ Angular's `keyframe()` function is similar to keyframes in CSS. Keyframes allow
The code for this color change might look like this.
<code-example path="animations/src/app/status-slider.component.ts" header="src/app/status-slider.component.ts" region="keyframes" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/status-slider.component.ts" header="src/app/status-slider.component.ts" region="keyframes" language="typescript"></code-example>
### Offset
@ -269,8 +258,7 @@ Here's an example of using keyframes to create a pulse effect:
The code snippet for this animation might look like this.
<code-example path="animations/src/app/open-close.component.1.ts" header="src/app/open-close.component.ts" region="trigger" language="typescript" linenums="false">
</code-example>
<code-example path="animations/src/app/open-close.component.1.ts" header="src/app/open-close.component.ts" region="trigger" language="typescript"></code-example>
### Animatable properties and units
@ -292,7 +280,7 @@ In these cases, you can use a special wildcard `*` property value under `style()
In this example, we have a trigger called `shrinkOut`, used when an HTML element leaves the page. The animation takes whatever height the element has before it leaves, and animates from that height to zero.
<code-example path="animations/src/app/hero-list-auto.component.ts" header="src/app/hero-list-auto.component.ts" region="auto-calc" language="typescript" linenums="false"></code-example>
<code-example path="animations/src/app/hero-list-auto.component.ts" header="src/app/hero-list-auto.component.ts" region="auto-calc" language="typescript"></code-example>
### Keyframes summary

View File

@ -30,8 +30,7 @@ For details about `tsconfig.json`, see the official
The [Setup](guide/setup-local) guide uses the following `tsconfig.json`:
<code-example path="getting-started/tsconfig.0.json" header="tsconfig.json" linenums="false">
</code-example>
<code-example path="getting-started/tsconfig.0.json" header="tsconfig.json"></code-example>
This file contains options and flags that are essential for Angular applications.
@ -95,8 +94,7 @@ like `Promise` if the target is `es6`.
By default, the target is `es2015`. If you are targeting `es5`, you still have newer type declarations due to the list of declaration files included:
<code-example path="getting-started/tsconfig.0.json" header="tsconfig.json (lib excerpt)" linenums="false" region="lib">
</code-example>
<code-example path="getting-started/tsconfig.0.json" header="tsconfig.json (lib excerpt)" region="lib"></code-example>
### Installable typings files
@ -116,4 +114,4 @@ For instance, to install typings for `jasmine` you run `npm install @types/jasmi
### *target*
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.

View File

@ -2,15 +2,15 @@
This guide describes **Angular Universal**, a technology that renders Angular applications on the server.
A normal Angular application executes in the _browser_, rendering pages in the DOM in response to user actions.
A normal Angular application executes in the _browser_, rendering pages in the DOM in response to user actions.
Angular Universal executes on the _server_, generating _static_ application pages that later get bootstrapped on
the client. This means that the application generally renders more quickly, giving users a chance to view the application
layout before it becomes fully interactive.
For a more detailed look at different techniques and concepts surrounding SSR, please check out this
For a more detailed look at different techniques and concepts surrounding SSR, please check out this
[article](https://developers.google.com/web/updates/2019/02/rendering-on-the-web).
You can easily prepare an app for server-side rendering using the [Angular CLI](guide/glossary#cli).
You can easily prepare an app for server-side rendering using the [Angular CLI](guide/glossary#cli).
The CLI schematic `@nguniversal/express-engine` performs the required steps, as described below.
<div class="alert is-helpful">
@ -21,9 +21,9 @@ The CLI schematic `@nguniversal/express-engine` performs the required steps, as
</div>
{@a the-example}
## Universal tutorial
## Universal tutorial
The [Tour of Heroes tutorial](tutorial) is the foundation for this walkthrough.
The [Tour of Heroes tutorial](tutorial) is the foundation for this walkthrough.
In this example, the Angular CLI compiles and bundles the Universal version of the app with the
[Ahead-of-Time (AoT) compiler](guide/aot-compiler).
@ -39,7 +39,7 @@ ng add @nguniversal/express-engine --clientProject angular.io-example
The command creates the following folder structure.
<code-example format="." language="none" linenums="false">
<code-example format="." language="none">
src/
index.html <i>app web page</i>
main.ts <i>bootstrapper for client app</i>
@ -62,7 +62,7 @@ The files marked with `*` are new and not in the original tutorial sample.
To start rendering your app with Universal on your local system, use the following command.
<code-example format="." language="bash" linenums="false">
<code-example format="." language="bash">
npm run build:ssr && npm run serve:ssr
</code-example>
@ -73,7 +73,7 @@ Navigation via `routerLinks` works correctly because they use the native anchor
You can go from the Dashboard to the Heroes page and back.
You can click a hero on the Dashboard page to display its Details page.
If you throttle your network speed so that the client-side scripts take longer to download (instructions below),
If you throttle your network speed so that the client-side scripts take longer to download (instructions below),
you'll notice:
* Clicking a hero on the Heroes page does nothing.
* You can't add or delete a hero.
@ -81,7 +81,7 @@ you'll notice:
* The *Back* and *Save* buttons on the Details page don't work.
User events other than `routerLink` clicks aren't supported.
You must wait for the full client app to bootstrap and run, or buffer the events using libraries like
You must wait for the full client app to bootstrap and run, or buffer the events using libraries like
[preboot](https://github.com/angular/preboot), which allow you to replay these events once the client-side scripts load.
The transition from the server-rendered app to the client app happens quickly on a development machine, but you should
@ -90,7 +90,7 @@ always test your apps in real-world scenarios.
You can simulate a slower network to see the transition more clearly as follows:
1. Open the Chrome Dev Tools and go to the Network tab.
1. Find the [Network Throttling](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling)
1. Find the [Network Throttling](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling)
dropdown on the far right of the menu bar.
1. Try one of the "3G" speeds.
@ -109,7 +109,7 @@ There are three main reasons to create a Universal version of your app.
{@a web-crawlers}
### Facilitate web crawlers (SEO)
Google, Bing, Facebook, Twitter, and other social media sites rely on web crawlers to index your application content and
Google, Bing, Facebook, Twitter, and other social media sites rely on web crawlers to index your application content and
make that content searchable on the web.
These web crawlers may be unable to navigate and index your highly interactive Angular application as a human user could do.
@ -128,7 +128,7 @@ people who otherwise couldn't use the app at all.
### Show the first page quickly
Displaying the first page quickly can be critical for user engagement.
[53 percent of mobile site visits are abandoned](https://www.thinkwithgoogle.com/marketing-resources/data-measurement/mobile-page-speed-new-industry-benchmarks/)
[53 percent of mobile site visits are abandoned](https://www.thinkwithgoogle.com/marketing-resources/data-measurement/mobile-page-speed-new-industry-benchmarks/)
if pages take longer than 3 seconds to load.
Your app may have to launch faster to engage these users before they decide to do something else.
@ -137,14 +137,14 @@ The pages are pure HTML, and can display even if JavaScript is disabled.
The pages don't handle browser events, but they _do_ support navigation through the site using [`routerLink`](guide/router#router-link).
In practice, you'll serve a static version of the landing page to hold the user's attention.
At the same time, you'll load the full Angular app behind it.
At the same time, you'll load the full Angular app behind it.
The user perceives near-instant performance from the landing page
and gets the full interactive experience after the full app loads.
{@a how-does-it-work}
## Universal web servers
A Universal web server responds to application page requests with static HTML rendered by the [Universal template engine](#universal-engine).
A Universal web server responds to application page requests with static HTML rendered by the [Universal template engine](#universal-engine).
The server receives and responds to HTTP requests from clients (usually browsers), and serves static assets such as scripts, CSS, and images.
It may respond to data requests, either directly or as a proxy to a separate data server.
@ -157,7 +157,7 @@ The sample web server for this guide is based on the popular [Express](https://e
</div>
Universal applications use the Angular `platform-server` package (as opposed to `platform-browser`), which provides
Universal applications use the Angular `platform-server` package (as opposed to `platform-browser`), which provides
server implementations of the DOM, `XMLHttpRequest`, and other low-level features that don't rely on a browser.
The server ([Node Express](https://expressjs.com/) in this guide's example)
@ -170,8 +170,8 @@ and a *route* that determines which components to display.
The route comes from the client's request to the server.
Each request results in the appropriate view for the requested route.
The `renderModuleFactory()` function renders the view within the `<app>` tag of the template,
creating a finished HTML page for the client.
The `renderModuleFactory()` function renders the view within the `<app>` tag of the template,
creating a finished HTML page for the client.
Finally, the server returns the rendered page to the client.
@ -179,11 +179,11 @@ Finally, the server returns the rendered page to the client.
Because a Universal app doesn't execute in the browser, some of the browser APIs and capabilities may be missing on the server.
For example, server-side applications can't reference browser-only global objects such as `window`, `document`, `navigator`, or `location`.
For example, server-side applications can't reference browser-only global objects such as `window`, `document`, `navigator`, or `location`.
Angular provides some injectable abstractions over these objects, such as [`Location`](api/common/Location)
Angular provides some injectable abstractions over these objects, such as [`Location`](api/common/Location)
or [`DOCUMENT`](api/common/DOCUMENT); it may substitute adequately for these APIs.
If Angular doesn't provide it, it's possible to write new abstractions that delegate to the browser APIs while in the browser
If Angular doesn't provide it, it's possible to write new abstractions that delegate to the browser APIs while in the browser
and to an alternative implementation while on the server (aka shimming).
Similarly, without mouse or keyboard events, a server-side app can't rely on a user clicking a button to show a component.
@ -196,7 +196,7 @@ This is a good argument for making the app [routable](guide/router).
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data.
These services send requests to _relative_ URLs such as `api/heroes`.
In a Universal app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`).
This means you need to change your services to make requests with absolute URLs when running on the server and with relative
This means you need to change your services to make requests with absolute URLs when running on the server and with relative
URLs when running in the browser.
One solution is to provide the full URL to your application on the server, and write an interceptor that can retrieve this
@ -263,26 +263,26 @@ The important bit in the `server.ts` file is the `ngExpressEngine()` function.
<code-example path="universal/server.ts" header="server.ts" region="ngExpressEngine">
</code-example>
The `ngExpressEngine()` function is a wrapper around Universal's `renderModuleFactory()` function which turns a client's
The `ngExpressEngine()` function is a wrapper around Universal's `renderModuleFactory()` function which turns a client's
requests into server-rendered HTML pages.
* The first parameter is `AppServerModule`.
It's the bridge between the Universal server-side renderer and the Angular application.
* The second parameter, `extraProviders`, is optional. It lets you specify dependency providers that apply only when
* The second parameter, `extraProviders`, is optional. It lets you specify dependency providers that apply only when
running on this server.
You can do this when your app needs information that can only be determined by the currently running server instance.
You can do this when your app needs information that can only be determined by the currently running server instance.
One example could be the running server's *origin*, which could be used to [calculate absolute HTTP URLs](#http-urls) if
not using the `Request` token as shown above.
The `ngExpressEngine()` function returns a `Promise` callback that resolves to the rendered page.
The `ngExpressEngine()` function returns a `Promise` callback that resolves to the rendered page.
It's up to the engine to decide what to do with that page.
This engine's `Promise` callback returns the rendered page to the web server,
which then forwards it to the client in the HTTP response.
<div class="alert is-helpful">
**Note:** These wrappers help hide the complexity of the `renderModuleFactory()` function. There are more wrappers
**Note:** These wrappers help hide the complexity of the `renderModuleFactory()` function. There are more wrappers
for different backend technologies at the [Universal repository](https://github.com/angular/universal).
</div>
@ -299,7 +299,7 @@ The browser could ask for one of the application routes such as `/dashboard`, `/
In fact, if the app were only rendered by the server, _every_ app link clicked would arrive at the server
as a navigation URL intended for the router.
Fortunately, application routes have something in common: their URLs lack file extensions.
Fortunately, application routes have something in common: their URLs lack file extensions.
(Data requests also lack extensions but they're easy to recognize because they always begin with `/api`.)
All static asset requests have a file extension (such as `main.js` or `/node_modules/zone.js/dist/zone.js`).
@ -309,11 +309,10 @@ Because we use routing, we can easily recognize the three types of requests and
1. **App navigation**: request URL with no file extension.
1. **Static asset**: all other requests.
A Node Express server is a pipeline of middleware that filters and processes requests one after the other.
A Node Express server is a pipeline of middleware that filters and processes requests one after the other.
You configure the Node Express server pipeline with calls to `app.get()` like this one for data requests.
<code-example path="universal/server.ts" header="server.ts (data URL)" region="data-request" linenums="false">
</code-example>
<code-example path="universal/server.ts" header="server.ts (data URL)" region="data-request"></code-example>
<div class="alert is-helpful">
@ -327,19 +326,17 @@ You configure the Node Express server pipeline with calls to `app.get()` like th
The following code filters for request URLs with no extensions and treats them as navigation requests.
<code-example path="universal/server.ts" header="server.ts (navigation)" region="navigation-request" linenums="false">
</code-example>
<code-example path="universal/server.ts" header="server.ts (navigation)" region="navigation-request"></code-example>
### Serving static files safely
A single `app.use()` treats all other URLs as requests for static assets
such as JavaScript, image, and style files.
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in
the `/dist` folder and only honor requests for files from the `/dist` folder.
The following Node Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the
The following Node Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the
file isn't found.
<code-example path="universal/server.ts" header="server.ts (static files)" region="static" linenums="false">
</code-example>
<code-example path="universal/server.ts" header="server.ts (static files)" region="static"></code-example>

View File

@ -840,15 +840,15 @@ After this, the service is injectable anywhere in AngularJS code:
## Lazy Loading AngularJS
When building applications, you want to ensure that only the required resources are loaded when necessary. Whether that be loading of assets or code, making sure everything that can be deferred until needed keeps your application running efficiently. This is especially true when running different frameworks in the same application.
When building applications, you want to ensure that only the required resources are loaded when necessary. Whether that be loading of assets or code, making sure everything that can be deferred until needed keeps your application running efficiently. This is especially true when running different frameworks in the same application.
[Lazy loading](guide/glossary#lazy-loading) is a technique that defers the loading of required assets and code resources until they are actually used. This reduces startup time and increases efficiency, especially when running different frameworks in the same application.
When migrating large applications from AngularJS to Angular using a hybrid approach, you want to migrate some of the most commonly used features first, and only use the less commonly used features if needed. Doing so helps you ensure that the application is still providing a seamless experience for your users while you are migrating.
In most environments where both Angular and AngularJS are used to render the application, both frameworks are loaded in the initial bundle being sent to the client. This results in both increased bundle size and possible reduced performance.
In most environments where both Angular and AngularJS are used to render the application, both frameworks are loaded in the initial bundle being sent to the client. This results in both increased bundle size and possible reduced performance.
Overall application performance is affected in cases where the user stays on Angular-rendered pages because the AngularJS framework and application are still loaded and running, even if they are never accessed.
Overall application performance is affected in cases where the user stays on Angular-rendered pages because the AngularJS framework and application are still loaded and running, even if they are never accessed.
You can take steps to mitigate both bundle size and performance issues. By isolating your AngularJS app to a separate bundle, you can take advantage of [lazy loading](guide/glossary#lazy-loading) to load, bootstrap, and render the AngularJS application only when needed. This strategy reduces your initial bundle size, defers any potential impact from loading both frameworks until absolutely necessary, and keeps your application running as efficiently as possible.
@ -908,7 +908,7 @@ When your application matches a route that needs AngularJS, the AngularJS app is
## Using the Unified Angular Location Service
In AngularJS, the [$location service](https://docs.angularjs.org/api/ng/service/$location) handles all routing configuration and navigation, encoding and decoding of URLS, redirects, and interactions with browser APIs. Angular uses its own underlying `Location` service for all of these tasks.
In AngularJS, the [$location service](https://docs.angularjs.org/api/ng/service/$location) handles all routing configuration and navigation, encoding and decoding of URLS, redirects, and interactions with browser APIs. Angular uses its own underlying `Location` service for all of these tasks.
When you migrate from AngularJS to Angular you will want to move as much responsibility as possible to Angular, so that you can take advantage of new APIs. To help with the transition, Angular provides the `LocationUpgradeModule`. This module enables a _unified_ location service that shifts responsibilities from the AngularJS `$location` service to the Angular `Location` service.
@ -1455,8 +1455,7 @@ Re-open the `app.module.ts` file, import and add `HttpClientModule` to the `impo
Now you're ready to upgrade the Phone service itself. Replace the ngResource-based
service in `phone.service.ts` with a TypeScript class decorated as `@Injectable`:
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="classdef" header="app/core/phone/phone.service.ts (skeleton)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="classdef" header="app/core/phone/phone.service.ts (skeleton)"></code-example>
The `@Injectable` decorator will attach some dependency injection metadata
to the class, letting Angular know about its dependencies. As described
@ -1475,14 +1474,12 @@ and the other loads the details of a specified phone:
The methods now return observables of type `PhoneData` and `PhoneData[]`. This is
a type you don't have yet. Add a simple interface for it:
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="phonedata-interface" header="app/core/phone/phone.service.ts (interface)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="phonedata-interface" header="app/core/phone/phone.service.ts (interface)"></code-example>
`@angular/upgrade/static` has a `downgradeInjectable` method for the purpose of making
Angular services available to AngularJS code. Use it to plug in the `Phone` service:
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="downgrade-injectable" header="app/core/phone/phone.service.ts (downgrade)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/core/phone/phone.service.ts" region="downgrade-injectable" header="app/core/phone/phone.service.ts (downgrade)"></code-example>
Here's the full, final code for the service:
@ -1551,15 +1548,13 @@ Now convert the template of this component into Angular syntax.
The search controls replace the AngularJS `$ctrl` expressions
with Angular's two-way `[(ngModel)]` binding syntax:
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.template.html" region="controls" header="app/phone-list/phone-list.template.html (search controls)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.template.html" region="controls" header="app/phone-list/phone-list.template.html (search controls)"></code-example>
Replace the list's `ng-repeat` with an `*ngFor` as
[described in the Template Syntax page](guide/template-syntax#directives).
Replace the image tag's `ng-src` with a binding to the native `src` property.
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.template.html" region="list" header="app/phone-list/phone-list.template.html (phones)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.template.html" region="list" header="app/phone-list/phone-list.template.html (phones)"></code-example>
#### No Angular _filter_ or _orderBy_ filters
@ -1611,8 +1606,7 @@ Do that in a new file called `ajs-upgraded-providers.ts` and import it in `app.m
<code-example path="upgrade-phonecat-2-hybrid/app/ajs-upgraded-providers.ts" header="app/ajs-upgraded-providers.ts">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/app.module.ts" region="routeparams" header="app/app.module.ts ($routeParams)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/app.module.ts" region="routeparams" header="app/app.module.ts ($routeParams)"></code-example>
Convert the phone detail component template into Angular syntax as follows:
@ -1661,8 +1655,7 @@ It's easy to turn the filter function into an equivalent Pipe class.
The implementation is the same as before, repackaged in the `transform` method.
Rename the file to `checkmark.pipe.ts` to conform with Angular conventions:
<code-example path="upgrade-phonecat-2-hybrid/app/core/checkmark/checkmark.pipe.ts" header="app/core/checkmark/checkmark.pipe.ts" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-2-hybrid/app/core/checkmark/checkmark.pipe.ts" header="app/core/checkmark/checkmark.pipe.ts"></code-example>
Now import and declare the newly created pipe and
remove the filter &lt;script&gt; tag from `index.html`:
@ -1726,8 +1719,7 @@ element on the host web page when the application launches.
Add this `<phonecat-app>` element to the `index.html`.
It replaces the old AngularJS `ng-view` directive:
<code-example path="upgrade-phonecat-3-final/index.html" region="appcomponent" header="index.html (body)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-3-final/index.html" region="appcomponent" header="index.html (body)"></code-example>
#### Create the _Routing Module_
A router needs configuration whether it's the AngularJS or Angular or any other router.
@ -1767,8 +1759,7 @@ You no longer have to hardcode the links to phone details in the phone list.
You can generate data bindings for each phone's `id` to the `routerLink` directive
and let that directive construct the appropriate URL to the `PhoneDetailComponent`:
<code-example path="upgrade-phonecat-3-final/app/phone-list/phone-list.template.html" region="list" header="app/phone-list/phone-list.template.html (list with links)" linenums="false">
</code-example>
<code-example path="upgrade-phonecat-3-final/app/phone-list/phone-list.template.html" region="list" header="app/phone-list/phone-list.template.html (list with links)"></code-example>
<div class="alert is-helpful">

View File

@ -20,9 +20,7 @@ To bind to a DOM event, surround the DOM event name in parentheses and assign a
The following example shows an event binding that implements a click handler:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" header="src/app/click-me.component.ts" linenums="false">
</code-example>
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" header="src/app/click-me.component.ts"></code-example>
{@a click}
@ -37,9 +35,7 @@ usually the Angular component controlling the template.
The example above shows a single line of HTML, but that HTML belongs to a larger component:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" header="src/app/click-me.component.ts" linenums="false">
</code-example>
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" header="src/app/click-me.component.ts"></code-example>
@ -53,18 +49,14 @@ This section shows how to bind to the `keyup` event of an input box to get the u
The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" header="src/app/keyup.components.ts (template v.1)" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" header="src/app/keyup.components.ts (template v.1)"></code-example>
When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding
DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" header="src/app/keyup.components.ts (class v.1)" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" header="src/app/keyup.components.ts (class v.1)"></code-example>
@ -126,9 +118,7 @@ that could reveal properties of the event object and prevent silly mistakes.
The following example rewrites the method with types:
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" header="src/app/keyup.components.ts (class v.1 - typed )" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" header="src/app/keyup.components.ts (class v.1 - typed )"></code-example>
@ -156,9 +146,7 @@ To declare a template reference variable, precede an identifier with a hash (or
The following example uses a template reference variable
to implement a keystroke loopback in a simple template.
<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" header="src/app/loop-back.component.ts" linenums="false">
</code-example>
<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" header="src/app/loop-back.component.ts"></code-example>
@ -200,9 +188,7 @@ It's easier to get to the input box with the template reference
variable than to go through the `$event` object. Here's a rewrite of the previous
`keyup` example that uses a template reference variable to get the user's input.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" header="src/app/keyup.components.ts (v2)" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" header="src/app/keyup.components.ts (v2)"></code-example>
@ -219,9 +205,7 @@ One way to reduce the noise would be to examine every `$event.keyCode` and take
There's an easier way: bind to Angular's `keyup.enter` pseudo-event.
Then Angular calls the event handler only when the user presses _Enter_.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" header="src/app/keyup.components.ts (v3)" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" header="src/app/keyup.components.ts (v3)"></code-example>
@ -244,9 +228,7 @@ The component's `value` property is updated only when the user presses _Enter_.
To fix this issue, listen to both the _Enter_ key and the _blur_ event.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" header="src/app/keyup.components.ts (v4)" linenums="false">
</code-example>
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" header="src/app/keyup.components.ts (v4)"></code-example>
@ -270,9 +252,7 @@ clicking **Add**.
Below is the "Little Tour of Heroes" component.
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" header="src/app/little-tour.component.ts" linenums="false">
</code-example>
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" header="src/app/little-tour.component.ts"></code-example>

View File

@ -17,7 +17,7 @@ The following properties, at the top level of the file, configure the workspace.
The initial app that you create with `ng new app_name` is listed under "projects":
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"projects": {
"app_name": {
@ -45,7 +45,7 @@ When you create a library project with `ng generate library`, the library projec
The following top-level configuration properties are available for each project, under `projects:<project_name>`.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"my-app": {
"root": "",
@ -118,7 +118,7 @@ Each target object specifies the `builder` for that target, which is the npm pac
In addition, each target has an `options` section that configures default options for the target, and a `configurations` section that names and specifies alternative configurations for the target.
See the example in [Build target](#build-target) below.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"architect": {
"build": { },
@ -206,7 +206,7 @@ The following sections provide more details of how these complex values are used
Each `build` target configuration can include an `assets` array that lists files or folders you want to copy as-is when building your project.
By default, the `src/assets/` folder and `src/favicon.ico` are copied over.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"assets": [
"src/assets",
@ -227,7 +227,7 @@ A asset specification object can have the following fields.
For example, the default asset paths can be represented in more detail using the following objects.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"assets": [
{ "glob": "**/*", "input": "src/assets/", "output": "/assets/" },
@ -239,7 +239,7 @@ For example, the default asset paths can be represented in more detail using the
You can use this extended configuration to copy assets from outside your project.
For example, the following configuration copies assets from a node package:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"assets": [
{ "glob": "**/*", "input": "./node_modules/some-package/images", "output": "/some-package/" },
@ -251,7 +251,7 @@ The contents of `node_modules/some-package/images/` will be available in `dist/s
The following example uses the `ignore` field to exclude certain files in the assets folder from being copied into the build:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"assets": [
{ "glob": "**/*", "input": "src/assets/", "ignore": ["**/*.svg"], "output": "/assets/" },
@ -270,7 +270,7 @@ With a configuration object, you have the option of naming the bundle for the en
The bundle is injected by default, but you can set `inject` to false to exclude the bundle from injection.
For example, the following object values create and name a bundle that contains styles and scripts, and excludes it from injection:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"styles": [
{ "input": "src/external-module/styles.scss", "inject": false, "bundleName": "external-module" }
@ -283,7 +283,7 @@ For example, the following object values create and name a bundle that contains
You can mix simple and complex file references for styles and scripts.
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"styles": [
"src/styles.css",
@ -302,7 +302,7 @@ In Sass and Stylus you can make use of the `includePaths` functionality for both
To add paths, use the `stylePreprocessorOptions` option:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"stylePreprocessorOptions": {
"includePaths": [
@ -335,7 +335,7 @@ You can supply an object as a configuration value for either of these to provide
* The flag `--optimization="true"` applies to both scripts and styles. You can supply a value such as the following to apply optimization to one or the other:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"optimization": { "scripts": true, "styles": false }
@ -346,7 +346,7 @@ You can configure the option to apply to one or the other.
You can also choose to output hidden source maps, or resolve vendor package source maps.
For example:
<code-example format="." language="json" linenums="false">
<code-example format="." language="json">
"sourceMaps": { "scripts": true, "styles": false, "hidden": true, "vendor": true }
@ -358,7 +358,7 @@ For example:
These are useful if you only want source maps to map error stack traces in error reporting tools,
but don't want to expose your source maps in the browser developer tools.
For [Universal](guide/glossary#universal), you can reduce the code rendered in the HTML page by
For [Universal](guide/glossary#universal), you can reduce the code rendered in the HTML page by
setting styles optimization to `true` and styles source maps to `false`.
</div>

View File

@ -1,10 +1,10 @@
# Managing Data
At the end of [Routing](start/routing "Getting Started: Routing"), the online store application has a product catalog with two views: a product list and product details.
Users can click on a product name from the list to see details in a new view, with a distinct URL (route).
At the end of [Routing](start/routing "Getting Started: Routing"), the online store application has a product catalog with two views: a product list and product details.
Users can click on a product name from the list to see details in a new view, with a distinct URL (route).
In this section, you'll create the shopping cart. You'll:
* Update the product details page to include a "Buy" button, which adds the current product to a list of products managed by a cart service.
* Update the product details page to include a "Buy" button, which adds the current product to a list of products managed by a cart service.
* Add a cart component, which displays the items you added to your cart.
* Add a shipping component, which retrieves shipping prices for the items in the cart by using Angular's `HttpClient` to retrieve shipping data from a `.json` file.
@ -18,14 +18,14 @@ Services are the place where you share data between parts of your application. F
{@a create-cart-service}
## Create the shopping cart service
Up to this point, users can view product information, and simulate sharing and being notified about product changes. They cannot, however, buy products.
Up to this point, users can view product information, and simulate sharing and being notified about product changes. They cannot, however, buy products.
In this section, you'll add a "Buy" button the product details page.
In this section, you'll add a "Buy" button the product details page.
You'll also set up a cart service to store information about products in the cart.
<div class="alert is-helpful">
Later, in the [Forms](start/forms "Getting Started: Forms") part of this tutorial, this cart service also will be accessed from the page where the user checks out.
Later, in the [Forms](start/forms "Getting Started: Forms") part of this tutorial, this cart service also will be accessed from the page where the user checks out.
</div>
@ -40,38 +40,38 @@ Later, in the [Forms](start/forms "Getting Started: Forms") part of this tutoria
1. If the generated `@Injectable()` decorator does not include the `{ providedIn: 'root' }` statement, then insert it as shown above.
1. In the `CartService` class, define an `items` property to store the list (array) of the current products in the cart.
1. In the `CartService` class, define an `items` property to store the list (array) of the current products in the cart.
<code-example path="getting-started/src/app/cart.service.ts" region="props"></code-example>
1. Define methods to add items to the cart, return cart items, and clear the cart items:
1. Define methods to add items to the cart, return cart items, and clear the cart items:
<code-example path="getting-started/src/app/cart.service.ts" region="methods" linenums="false"></code-example>
<code-example path="getting-started/src/app/cart.service.ts" region="methods"></code-example>
<!--
To check: StackBlitz includes the constructor. If it's important (and not obvious) that the methods be below the constructor, then we should show it or say something.
To check: StackBlitz includes the constructor. If it's important (and not obvious) that the methods be below the constructor, then we should show it or say something.
-->
<!--
* The `addToCart()` method appends a product to an array of `items`.
<!--
* The `addToCart()` method appends a product to an array of `items`.
* The `getItems()` method collects the items added to the cart and returns each item with its associated quantity.
* The `clearCart()` method returns an empty array of items.
* The `clearCart()` method returns an empty array of items.
-->
{@a product-details-use-cart-service}
### Use the cart service
### Use the cart service
In this section, you'll update the product details component to use the cart service.
You'll add a "Buy" button to the product details view.
When the "Buy" button is clicked, you'll use the cart service to add the current product to the cart.
In this section, you'll update the product details component to use the cart service.
You'll add a "Buy" button to the product details view.
When the "Buy" button is clicked, you'll use the cart service to add the current product to the cart.
1. Open `product-details.component.ts`.
1. Set up the component to be able to use the cart service.
1. Set up the component to be able to use the cart service.
1. Import the cart service.
1. Import the cart service.
<code-example header="src/app/product-details/product-details.component.ts" path="getting-started/src/app/product-details/product-details.component.ts" region="cart-service">
</code-example>
@ -81,24 +81,24 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
<code-example path="getting-started/src/app/product-details/product-details.component.ts" region="inject-cart-service">
</code-example>
<!--
<!--
To do: Consider defining "inject" and describing the concept of "dependency injection"
-->
1. Define the `addToCart()` method, which adds the current product to the cart.
1. Define the `addToCart()` method, which adds the current product to the cart.
The `addToCart()` method:
* Receives the current `product`
* Uses the cart service's `#addToCart()` method to add the product the cart
* Displays a message that the product has been added to the cart
<code-example path="getting-started/src/app/product-details/product-details.component.ts" region="add-to-cart"></code-example>
1. Update the product details template to have a "Buy" button that adds the current product to the cart.
1. Update the product details template to have a "Buy" button that adds the current product to the cart.
1. Open `product-details.component.html`.
1. Add a button with the label "Buy", and bind the `click()` event to the `addToCart()` method:
1. Add a button with the label "Buy", and bind the `click()` event to the `addToCart()` method:
<code-example header="src/app/product-details/product-details.component.html" path="getting-started/src/app/product-details/product-details.component.html">
</code-example>
@ -108,8 +108,8 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
<figure>
<img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button">
</figure>
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
<figure>
<img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button">
@ -118,35 +118,35 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
## Create the cart page
At this point, users can put items in the cart by clicking "Buy", but they can't yet see their cart.
At this point, users can put items in the cart by clicking "Buy", but they can't yet see their cart.
We'll create the cart page in two steps:
We'll create the cart page in two steps:
1. Create a cart component and set up routing to the new component. At this point, the cart page will only have default text.
1. Display the cart items.
1. Create a cart component and set up routing to the new component. At this point, the cart page will only have default text.
1. Display the cart items.
### Set up the component
To create the cart page, you begin by following the same steps you did to create the product details component and to set up routing for the new component.
1. Generate a cart component, named `cart`.
1. Generate a cart component, named `cart`.
Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`.
Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`.
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.1.ts"></code-example>
1. Add routing (a URL pattern) for the cart component.
1. Add routing (a URL pattern) for the cart component.
Reminder: Open `app.module.ts` and add a route for the component `CartComponent`, with a `path` of `cart`:
<code-example header="src/app/app.module.ts" path="getting-started/src/app/app.module.ts" region="cart-route">
</code-example>
<!--
To do: Can we shorten the example code to remove the extra at the bottom?
<!--
To do: Can we shorten the example code to remove the extra at the bottom?
-->
1. To see the new cart component, click the "Checkout" button. You can see the "cart works!" default text, and the URL has the pattern `https://getting-started.stackblitz.io/cart`, where `getting-started.stackblitz.io` may be different for your StackBlitz project.
1. To see the new cart component, click the "Checkout" button. You can see the "cart works!" default text, and the URL has the pattern `https://getting-started.stackblitz.io/cart`, where `getting-started.stackblitz.io` may be different for your StackBlitz project.
(Note: The "Checkout" button that we provided in the top-bar component was already configured with a `routerLink` for `/cart`.)
@ -155,7 +155,7 @@ We'll create the cart page in two steps:
</figure>
### Display the cart items
### Display the cart items
Services can be used to share data across components:
@ -184,25 +184,25 @@ Services can be used to share data across components:
1. Set the items using the cart service's `getItems()` method. (You defined this method [when you generated `cart.service.ts`](#generate-cart-service).)
The resulting `CartComponent` class should look like this:
The resulting `CartComponent` class should look like this:
<code-example path="getting-started/src/app/cart/cart.component.3.ts" region="props-services">
</code-example>
1. Update the template with a header ("Cart"), and use a `<div>` with an `*ngFor` to display each of the cart items with its name and price.
The resulting `CartComponent` template should look like this:
The resulting `CartComponent` template should look like this:
<code-example header="src/app/cart/cart.component.html" path="getting-started/src/app/cart/cart.component.2.html" region="prices">
</code-example>
1. Test your cart component.
1. Test your cart component.
1. Click on "My Store" to go to the product list page.
1. Click on a product name to display its details.
1. Click "Buy" to add the product to the cart.
1. Click "Checkout" to see the cart.
1. To add another product, click "My Store" to return to the product list. Repeat the steps above.
1. Click "Checkout" to see the cart.
1. To add another product, click "My Store" to return to the product list. Repeat the steps above.
<figure>
<img src='generated/images/guide/start/cart-page-full.png' alt="Cart page with products added">
@ -211,17 +211,17 @@ Services can be used to share data across components:
<div class="alert is-helpful">
StackBlitz tip: Any time the preview refreshes, the cart is cleared. If you make changes to the app, the page refreshes, and you'll need to buy products again to populate the cart.
StackBlitz tip: Any time the preview refreshes, the cart is cleared. If you make changes to the app, the page refreshes, and you'll need to buy products again to populate the cart.
</div>
<!--
To do: New screen shot. No shipping prices link yet. Show a few products in the cart.
<!--
To do: New screen shot. No shipping prices link yet. Show a few products in the cart.
-->
<div class="alert is-helpful">
Learn more: See [Introduction to Services and Dependency Injection](guide/architecture-services "Architecture > Intro to Services and DI") for more information about services.
Learn more: See [Introduction to Services and Dependency Injection](guide/architecture-services "Architecture > Intro to Services and DI") for more information about services.
</div>
@ -230,16 +230,16 @@ Learn more: See [Introduction to Services and Dependency Injection](guide/archit
## Retrieve shipping prices
<!-- Accessing data with the HTTP client -->
Data returned from servers often takes the form of a stream.
Streams are useful because they make it easy to transform the data that is returned, and to make modifications to the way data is requested.
Data returned from servers often takes the form of a stream.
Streams are useful because they make it easy to transform the data that is returned, and to make modifications to the way data is requested.
The Angular HTTP client (`HttpClient`) is a built-in way to fetch data from external APIs and provide them to your application as a stream.
In this section, you'll use the HTTP client to retrieve shipping prices from an external file.
In this section, you'll use the HTTP client to retrieve shipping prices from an external file.
### Predefined shipping data
For the purpose of this Getting Started guide, we have provided shipping data in `assets/shipping.json`.
You'll use this data to add shipping prices for items in the cart.
For the purpose of this Getting Started guide, we have provided shipping data in `assets/shipping.json`.
You'll use this data to add shipping prices for items in the cart.
<code-example header="src/assets/shipping.json" path="getting-started/src/assets/shipping.json">
</code-example>
@ -247,14 +247,14 @@ You'll use this data to add shipping prices for items in the cart.
### Enable HttpClient for app
Before you can use Angular's HTTP client, you must set up your app to use `HttpClientModule`.
Before you can use Angular's HTTP client, you must set up your app to use `HttpClientModule`.
Angular's `HttpClientModule` registers the providers needed to use a single instance of the `HttpClient` service throughout your app.
The `HttpClient` service is what you inject into your services to fetch data and interact with external APIs and resources.
Angular's `HttpClientModule` registers the providers needed to use a single instance of the `HttpClient` service throughout your app.
The `HttpClient` service is what you inject into your services to fetch data and interact with external APIs and resources.
1. Open `app.module.ts`.
1. Open `app.module.ts`.
This file contains imports and functionality that is available to the entire app.
This file contains imports and functionality that is available to the entire app.
1. Import `HttpClientModule` from the `@angular/common/http` package.
@ -268,15 +268,15 @@ The `HttpClient` service is what you inject into your services to fetch data and
<code-example path="getting-started/src/app/app.module.ts" region="http-client-module">
</code-example>
<!--
<!--
To do: Should ReactiveFormsModule already be here? Currently, it is in the starter stackblitz, so this doc assumes it is already included and not added in the forms section.
-->
<!--
To do: Should ReactiveFormsModule already be here?
<!--
To do: Should ReactiveFormsModule already be here?
-->
### Enable HttpClient for cart service
### Enable HttpClient for cart service
1. Open `cart.service.ts`.
@ -285,7 +285,7 @@ To do: Should ReactiveFormsModule already be here?
<code-example header="src/app/cart.service.ts" path="getting-started/src/app/cart.service.ts" region="import-http">
</code-example>
1. Inject `HttpClient` into the constructor of the `CartService` component class:
1. Inject `HttpClient` into the constructor of the `CartService` component class:
<code-example path="getting-started/src/app/cart.service.ts" region="inject-http">
</code-example>
@ -293,8 +293,8 @@ To do: Should ReactiveFormsModule already be here?
### Define the get() method
As you've seen, multiple components can leverage the same service.
Later in this tutorial, the shipping component will use the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
As you've seen, multiple components can leverage the same service.
Later in this tutorial, the shipping component will use the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
Here you'll define the `get()` method that will be used.
1. Continue working in `cart.service.ts`.
@ -315,31 +315,31 @@ Learn more: See the [HttpClient guide](guide/http "HttpClient guide") for more i
## Define the shipping page
Now that your app can retrieve shipping data, you'll create a shipping component and associated template.
Now that your app can retrieve shipping data, you'll create a shipping component and associated template.
1. Generate a new component named `shipping`.
Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`.
Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`.
<code-example header="src/app/shipping/shipping.component.ts" path="getting-started/src/app/shipping/shipping.component.1.ts"></code-example>
1. In `app.module.ts`, add a route for shipping. Specify a `path` of `shipping` and a component of `ShippingComponent`.
1. In `app.module.ts`, add a route for shipping. Specify a `path` of `shipping` and a component of `ShippingComponent`.
<code-example header="src/app/app.module.ts" path="getting-started/src/app/app.module.ts" region="shipping-route"></code-example>
The new shipping component isn't hooked into any other component yet, but you can see it in the preview pane by entering the URL specified by its route. The URL has the pattern: `https://getting-started.stackblitz.io/shipping` where the `getting-started.stackblitz.io` part may be different for your StackBlitz project.
The new shipping component isn't hooked into any other component yet, but you can see it in the preview pane by entering the URL specified by its route. The URL has the pattern: `https://getting-started.stackblitz.io/shipping` where the `getting-started.stackblitz.io` part may be different for your StackBlitz project.
1. Modify the shipping component so it uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
1. Modify the shipping component so it uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
1. Import the cart service.
<code-example header="src/app/shipping/shipping.component.ts" path="getting-started/src/app/shipping/shipping.component.ts" region="imports"></code-example>
1. Define a `shippingCosts` property.
1. Define a `shippingCosts` property.
<code-example path="getting-started/src/app/shipping/shipping.component.ts" region="props"></code-example>
1. Inject the cart service into the `ShippingComponent` class:
1. Inject the cart service into the `ShippingComponent` class:
<code-example path="getting-started/src/app/shipping/shipping.component.ts" region="inject-cart-service"></code-example>
@ -360,7 +360,7 @@ Now that your app can retrieve shipping data, you'll create a shipping component
<code-example header="src/app/cart/cart.component.html" path="getting-started/src/app/cart/cart.component.2.html"></code-example>
1. Test your shipping prices feature:
Click on the "Checkout" button to see the updated cart. (Remember that changing the app causes the preview to refresh, which empties the cart.)
<figure>
@ -376,10 +376,10 @@ Now that your app can retrieve shipping data, you'll create a shipping component
## Next steps
Congratulations! You have an online store application with a product catalog and shopping cart. You also have the ability to look up and display shipping prices.
Congratulations! You have an online store application with a product catalog and shopping cart. You also have the ability to look up and display shipping prices.
To continue exploring Angular, choose either of the following options:
* [Continue to the "Forms" section](start/forms "Getting Started: Forms") to finish the app by adding the shopping cart page and a form-based checkout feature. You'll create a form to collect user information as part of checkout.
* [Skip ahead to the "Deployment" section](start/deployment "Getting Started: Deployment") to move to local development, or deploy your app to Firebase or your own server.
* [Continue to the "Forms" section](start/forms "Getting Started: Forms") to finish the app by adding the shopping cart page and a form-based checkout feature. You'll create a form to collect user information as part of checkout.
* [Skip ahead to the "Deployment" section](start/deployment "Getting Started: Deployment") to move to local development, or deploy your app to Firebase or your own server.

View File

@ -2,7 +2,7 @@
At the end of [Managing Data](start/data "Getting Started: Managing Data"), the online store application has a product catalog and a shopping cart.
In this section, you'll finish the app by adding a form-based checkout feature. You'll create a form to collect user information as part of checkout.
In this section, you'll finish the app by adding a form-based checkout feature. You'll create a form to collect user information as part of checkout.
## Forms in Angular
@ -10,11 +10,11 @@ Forms in Angular take the standard capabilities of the HTML based forms and add
## Define the checkout form model
First, you'll set up the checkout form model. The form model is the source of truth for the status of the form and is defined in the component class.
First, you'll set up the checkout form model. The form model is the source of truth for the status of the form and is defined in the component class.
1. Open `cart.component.ts`.
1. Angular's `FormBuilder` service provides convenient methods for generating controls. As with the other services you've used, you need to import and inject the service before you can use it:
1. Angular's `FormBuilder` service provides convenient methods for generating controls. As with the other services you've used, you need to import and inject the service before you can use it:
1. Import the `FormBuilder` service from the `@angular/forms` package.
@ -23,7 +23,7 @@ First, you'll set up the checkout form model. The form model is the source of tr
The `FormBuilder` service is provided by the `ReactiveFormsModule`, which is already defined in the `AppModule` you modified previously (in `app.module.ts`).
1. Inject the `FormBuilder` service.
1. Inject the `FormBuilder` service.
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.ts" region="inject-form-builder">
</code-example>
@ -35,14 +35,13 @@ First, you'll set up the checkout form model. The form model is the source of tr
1. During checkout, the app will prompt the user for a name and address. So that you can gather that information later, set the `checkoutForm` property with a form model containing `name` and `address` fields, using the `FormBuilder#group()` method.
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.ts" region="checkout-form-group" linenums="false">
</code-example>
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.ts" region="checkout-form-group"></code-example>
1. For the checkout process, users need to be able to submit the form data (their name and address). When the order is submitted, the form should reset and the cart should clear.
1. For the checkout process, users need to be able to submit the form data (their name and address). When the order is submitted, the form should reset and the cart should clear.
In `cart.component.ts`, define an `onSubmit()` method to process the form. Use the `CartService#clearCart()` method to empty the cart items and reset the form after it is submitted. (In a real-world app, this method also would submit the data to an external server.)
In `cart.component.ts`, define an `onSubmit()` method to process the form. Use the `CartService#clearCart()` method to empty the cart items and reset the form after it is submitted. (In a real-world app, this method also would submit the data to an external server.)
The entire cart component is shown below:
The entire cart component is shown below:
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.ts">
</code-example>
@ -51,13 +50,13 @@ The form model is defined in the component class. To reflect the model in the vi
## Create the checkout form
Next, you'll add a checkout form at the bottom of the "Cart" page.
Next, you'll add a checkout form at the bottom of the "Cart" page.
1. Open `cart.component.html`.
1. At the bottom of the template, add an empty HTML form to capture user information.
1. At the bottom of the template, add an empty HTML form to capture user information.
1. Use a `formGroup` property binding to bind the `checkoutForm` to the `form` tag in the template. Also include a "Purchase" button to submit the form.
1. Use a `formGroup` property binding to bind the `checkoutForm` to the `form` tag in the template. Also include a "Purchase" button to submit the form.
<code-example header="src/app/cart/cart.component.html" path="getting-started/src/app/cart/cart.component.3.html" region="checkout-form">
</code-example>
@ -67,12 +66,12 @@ Next, you'll add a checkout form at the bottom of the "Cart" page.
<code-example path="getting-started/src/app/cart/cart.component.html" region="checkout-form-1">
</code-example>
1. Add input fields for `name` and `address`. Use the `formControlName` attribute binding to bind the `checkoutForm` form controls for `name` and `address` to their input fields. The final complete component is shown below:
1. Add input fields for `name` and `address`. Use the `formControlName` attribute binding to bind the `checkoutForm` form controls for `name` and `address` to their input fields. The final complete component is shown below:
<code-example path="getting-started/src/app/cart/cart.component.html" region="checkout-form-2">
</code-example>
After putting a few items in the cart, users can now review their items, enter name and address, and submit their purchase:
After putting a few items in the cart, users can now review their items, enter name and address, and submit their purchase:
<figure>
<img src='generated/images/guide/start/cart-with-items-and-form.png' alt="Cart page with checkout form">

View File

@ -83,16 +83,14 @@ You'll find the implementation of the shell `AppComponent` distributed over thre
Open the component class file (`app.component.ts`) and change the value of the `title` property to 'Tour of Heroes'.
<code-example path="toh-pt0/src/app/app.component.ts" region="set-title" header="app.component.ts (class title property)" linenums="false">
</code-example>
<code-example path="toh-pt0/src/app/app.component.ts" region="set-title" header="app.component.ts (class title property)"></code-example>
Open the component template file (`app.component.html`) and
delete the default template generated by the Angular CLI.
Replace it with the following line of HTML.
<code-example path="toh-pt0/src/app/app.component.html"
header="app.component.html (template)" linenums="false">
</code-example>
header="app.component.html (template)"></code-example>
The double curly braces are Angular's *interpolation binding* syntax.
This interpolation binding presents the component's `title` property value

View File

@ -17,8 +17,7 @@ the three files of the `HeroesComponent` along with a test file.
The `HeroesComponent` class file is as follows:
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1" header="app/heroes/heroes.component.ts (initial version)" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1" header="app/heroes/heroes.component.ts (initial version)"></code-example>
You always import the `Component` symbol from the Angular core library
and annotate the component class with `@Component`.
@ -46,8 +45,7 @@ Always `export` the component class so you can `import` it elsewhere ... like in
Add a `hero` property to the `HeroesComponent` for a hero named "Windstorm."
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="add-hero" header="heroes.component.ts (hero property)" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="add-hero" header="heroes.component.ts (hero property)"></code-example>
### Show the hero
@ -55,8 +53,7 @@ Open the `heroes.component.html` template file.
Delete the default text generated by the Angular CLI and
replace it with a data binding to the new `hero` property.
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" header="heroes.component.html" region="show-hero-1" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" header="heroes.component.html" region="show-hero-1"></code-example>
## Show the `HeroesComponent` view
@ -65,8 +62,7 @@ To display the `HeroesComponent`, you must add it to the template of the shell `
Remember that `app-heroes` is the [element selector](#selector) for the `HeroesComponent`.
So add an `<app-heroes>` element to the `AppComponent` template file, just below the title.
<code-example path="toh-pt1/src/app/app.component.html" header="src/app/app.component.html" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/app.component.html" header="src/app/app.component.html"></code-example>
Assuming that the CLI `ng serve` command is still running,
the browser should refresh and display both the application title and the hero name.
@ -78,8 +74,7 @@ A real hero is more than a name.
Create a `Hero` class in its own file in the `src/app` folder.
Give it `id` and `name` properties.
<code-example path="toh-pt1/src/app/hero.ts" header="src/app/hero.ts" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/hero.ts" header="src/app/hero.ts"></code-example>
Return to the `HeroesComponent` class and import the `Hero` class.
@ -89,9 +84,7 @@ Initialize it with an `id` of `1` and the name `Windstorm`.
The revised `HeroesComponent` class file should look like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" linenums="false"
header= "src/app/heroes/heroes.component.ts">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" header="src/app/heroes/heroes.component.ts"></code-example>
The page no longer displays properly because you changed the hero from a string to an object.
@ -100,8 +93,7 @@ The page no longer displays properly because you changed the hero from a string
Update the binding in the template to announce the hero's name
and show both `id` and `name` in a details layout like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="show-hero-2" header="heroes.component.html (HeroesComponent's template)" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="show-hero-2" header="heroes.component.html (HeroesComponent's template)"></code-example>
The browser refreshes and displays the hero's information.
@ -135,9 +127,7 @@ To automate that data flow, setup a two-way data binding between the `<input>` f
Refactor the details area in the `HeroesComponent` template so it looks like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="name-input" header="src/app/heroes/heroes.component.html (HeroesComponent's template)" linenums="false">
</code-example>
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="name-input" header="src/app/heroes/heroes.component.html (HeroesComponent's template)"></code-example>
**[(ngModel)]** is Angular's two-way data binding syntax.

View File

@ -15,9 +15,7 @@ Create a file called `mock-heroes.ts` in the `src/app/` folder.
Define a `HEROES` constant as an array of ten heroes and export it.
The file should look like this.
<code-example path="toh-pt2/src/app/mock-heroes.ts" linenums="false"
header="src/app/mock-heroes.ts">
</code-example>
<code-example path="toh-pt2/src/app/mock-heroes.ts" header="src/app/mock-heroes.ts"></code-example>
## Displaying heroes
@ -42,8 +40,7 @@ Open the `HeroesComponent` template file and make the following changes:
Make it look like this:
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="list" header="heroes.component.html (heroes template)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="list" header="heroes.component.html (heroes template)"></code-example>
That shows one hero. To list them all, add an `*ngFor` to the `<li>` to iterate through the list of heroes:
@ -117,8 +114,7 @@ and update the hero detail.
Add a click event binding to the `<li>` like this:
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="selectedHero-click" header="heroes.component.html (template excerpt)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="selectedHero-click" header="heroes.component.html (template excerpt)"></code-example>
This is an example of Angular's [event binding](guide/template-syntax#event-binding) syntax.
@ -138,8 +134,7 @@ There is no _selected hero_ when the application starts.
Add the following `onSelect()` method, which assigns the clicked hero from the template
to the component's `selectedHero`.
<code-example path="toh-pt2/src/app/heroes/heroes.component.ts" region="on-select" header="src/app/heroes/heroes.component.ts (onSelect)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.ts" region="on-select" header="src/app/heroes/heroes.component.ts (onSelect)"></code-example>
### Add a details section
@ -147,8 +142,7 @@ Currently, you have a list in the component template. To click on a hero on the
and reveal details about that hero, you need a section for the details to render in the
template. Add the following to `heroes.component.html` beneath the list section:
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="selectedHero-details" header="heroes.component.html (selected hero details)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="selectedHero-details" header="heroes.component.html (selected hero details)"></code-example>
After the browser refreshes, the application is broken.
@ -180,8 +174,7 @@ Don't forget the asterisk (*) in front of `ngIf`. It's a critical part of the sy
</div>
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="ng-if" header="src/app/heroes/heroes.component.html (*ngIf)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="ng-if" header="src/app/heroes/heroes.component.html (*ngIf)"></code-example>
After the browser refreshes, the list of names reappears.
The details area is blank.
@ -217,16 +210,13 @@ Just add `[class.some-css-class]="some-condition"` to the element you want to st
Add the following `[class.selected]` binding to the `<li>` in the `HeroesComponent` template:
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="class-selected" header="heroes.component.html (toggle the 'selected' CSS class)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="class-selected" header="heroes.component.html (toggle the 'selected' CSS class)"></code-example>
When the current row hero is the same as the `selectedHero`, Angular adds the `selected` CSS class. When the two heroes are different, Angular removes the class.
The finished `<li>` looks like this:
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="li" header="heroes.component.html (list item hero)" linenums="false">
</code-example>
<code-example path="toh-pt2/src/app/heroes/heroes.component.html" region="li" header="heroes.component.html (list item hero)"></code-example>
{@a final-code-review}

View File

@ -42,9 +42,7 @@ So replace "selectedHero" with "hero" everywhere in the template.
When you're done, the `HeroDetailComponent` template should look like this:
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.html" header="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.html" header="src/app/hero-detail/hero-detail.component.html"></code-example>
### Add the `@Input()` hero property
@ -67,13 +65,11 @@ because the _external_ `HeroesComponent` [will bind to it](#heroes-component-tem
Amend the `@angular/core` import statement to include the `Input` symbol.
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.ts" region="import-input" header="src/app/hero-detail/hero-detail.component.ts (import Input)" linenums="false">
</code-example>
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.ts" region="import-input" header="src/app/hero-detail/hero-detail.component.ts (import Input)"></code-example>
Add a `hero` property, preceded by the `@Input()` decorator.
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="input-hero" linenums="false">
</code-example>
<code-example path="toh-pt3/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="input-hero"></code-example>
That's the only change you should make to the `HeroDetailComponent` class.
There are no more properties. There's no presentation logic.
@ -117,8 +113,7 @@ and the `HeroDetailComponent` displays the new hero.
The revised `HeroesComponent` template should look like this:
<code-example path="toh-pt3/src/app/heroes/heroes.component.html"
header="heroes.component.html" linenums="false">
</code-example>
header="heroes.component.html"></code-example>
The browser refreshes and the app starts working again as it did before.

View File

@ -33,8 +33,7 @@ Using the Angular CLI, create a service called `hero`.
The command generates a skeleton `HeroService` class in `src/app/hero.service.ts` as follows:
<code-example path="toh-pt4/src/app/hero.service.1.ts" region="new"
header="src/app/hero.service.ts (new service)" linenums="false">
</code-example>
header="src/app/hero.service.ts (new service)"></code-example>
### `@Injectable()` services

View File

@ -349,7 +349,7 @@ the `onSelect()` method and `selectedHero` property are no longer used.
It's nice to tidy up and you'll be grateful to yourself later.
Here's the class after pruning away the dead code.
<code-example path="toh-pt5/src/app/heroes/heroes.component.ts" region="class" header="src/app/heroes/heroes.component.ts (cleaned up)" linenums="false">
<code-example path="toh-pt5/src/app/heroes/heroes.component.ts" region="class" header="src/app/heroes/heroes.component.ts (cleaned up)">
</code-example>
## Routable `HeroDetailComponent`

View File

@ -73,7 +73,7 @@ Generate the class `src/app/in-memory-data.service.ts` with the following comman
Replace the default contents of `in-memory-data.service.ts` with the following:
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" header="src/app/in-memory-data.service.ts" linenums="false"></code-example>
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" header="src/app/in-memory-data.service.ts"></code-example>
The `in-memory-data.service.ts` file replaces `mock-heroes.ts`, which is now safe to delete.
@ -390,8 +390,7 @@ the URL, which includes a query string with the search term.
Open the `DashboardComponent` template and
add the hero search element, `<app-hero-search>`, to the bottom of the markup.
<code-example path="toh-pt6/src/app/dashboard/dashboard.component.html" header="src/app/dashboard/dashboard.component.html" linenums="false">
</code-example>
<code-example path="toh-pt6/src/app/dashboard/dashboard.component.html" header="src/app/dashboard/dashboard.component.html"></code-example>
This template looks a lot like the `*ngFor` repeater in the `HeroesComponent` template.

View File

@ -4,9 +4,7 @@
module.exports = function code(h, node) {
var value = node.value ? ('\n' + node.value + '\n') : '';
var lang = node.lang && node.lang.match(/^[^ \t]+(?=[ \t]|$)/);
var props = {
linenums: 'false'
};
var props = {};
if (lang) {
props.language = lang;

View File

@ -88,7 +88,7 @@ describe('remark: renderMarkdown service', () => {
'```';
const output = renderMarkdown(content);
expect(output).toEqual(
'<code-example linenums="false" language="ts">\n' +
'<code-example language="ts">\n' +
' class MyClass {\n' +
' method1() { ... }\n' +
' }\n' +

View File

@ -2,7 +2,7 @@
{% import "lib/descendants.html" as descendants -%}
<section class="{$ doc.docType $}-overview">
<code-example language="ts" hideCopy="true" linenums="false">
<code-example language="ts" hideCopy="true">
{% if doc.isAbstract %}abstract {% endif%}class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {{$ memberHelper.renderMembers(doc) $}
}
</code-example>

View File

@ -3,7 +3,7 @@
{% for ngModule in doc.ngModules %}
<li>
<a href="{$ ngModule.path $}">
<code-example language="ts" hideCopy="true" linenums="false" class="no-box">{$ ngModule.name | escape $}</code-example>
<code-example language="ts" hideCopy="true" class="no-box">{$ ngModule.name | escape $}</code-example>
</a>
</li>
{% endfor %}

View File

@ -46,7 +46,7 @@
{$ overload.shortDescription | marked $}
</div>{% endif %}
<code-example language="ts" hideCopy="true" linenums="false" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
<code-example language="ts" hideCopy="true" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
{% if overload.deprecated !== undefined %}
<div class="deprecated">

View File

@ -16,7 +16,7 @@
{% for item in items %}
<tr>
<td>
<code-example language="ts" hideCopy="true" linenums="false" class="no-box">
<code-example language="ts" hideCopy="true" class="no-box">
{$ item | escape $}
</code-example>
</td>
@ -39,7 +39,7 @@
<tr>
<td>
<a href="{$ item.path $}">
<code-example language="ts" hideCopy="true" linenums="false" class="no-box{% if item.deprecated !== undefined %} deprecated-api-item{% endif %}">{$ item.name | escape $}</code-example>
<code-example language="ts" hideCopy="true" class="no-box{% if item.deprecated !== undefined %} deprecated-api-item{% endif %}">{$ item.name | escape $}</code-example>
</a>
</td>
<td>

View File

@ -46,8 +46,7 @@ export interface InjectableDecorator {
* The following example shows how a service class is properly
* marked so that a supporting service can be injected upon creation.
*
* <code-example path="core/di/ts/metadata_spec.ts" region="Injectable"
* linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="Injectable"></code-example>
*
*/
(): TypeDecorator;

View File

@ -29,11 +29,11 @@ export interface ValueSansProvider {
*
* ### Example
*
* {@example core/di/ts/provider_spec.ts region='ValueProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='ValueProvider'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/
@ -80,11 +80,11 @@ export interface StaticClassSansProvider {
*
* Note that following two providers are not equal:
*
* {@example core/di/ts/provider_spec.ts region='StaticClassProviderDifference' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='StaticClassProviderDifference'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/
@ -129,11 +129,11 @@ export interface ConstructorSansProvider {
*
* @usageNotes
*
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/
@ -172,11 +172,11 @@ export interface ExistingSansProvider {
*
* @usageNotes
*
* {@example core/di/ts/provider_spec.ts region='ExistingProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='ExistingProvider'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/
@ -221,15 +221,15 @@ export interface FactorySansProvider {
*
* @usageNotes
*
* {@example core/di/ts/provider_spec.ts region='FactoryProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='FactoryProvider'}
*
* Dependencies can also be marked as optional:
*
* {@example core/di/ts/provider_spec.ts region='FactoryProviderOptionalDeps' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='FactoryProviderOptionalDeps'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/
@ -266,7 +266,7 @@ export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvi
*
* @usageNotes
*
* {@example core/di/ts/provider_spec.ts region='TypeProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
*
* @publicApi
*/
@ -293,15 +293,15 @@ export interface ClassSansProvider {
*
* @usageNotes
*
* {@example core/di/ts/provider_spec.ts region='ClassProvider' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='ClassProvider'}
*
* Note that following two providers are not equal:
*
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference'}
*
* ### Multi-value example
*
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect' linenums="false"}
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
*
* @publicApi
*/

View File

@ -29,8 +29,8 @@ export interface InjectDecorator {
* When `@Inject()` is not present, the injector uses the type annotation of the
* parameter as the provider.
*
* <code-example path="core/di/ts/metadata_spec.ts"
* region="InjectWithoutDecorator" linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="InjectWithoutDecorator">
* </code-example>
*/
(token: any): any;
new (token: any): Inject;
@ -77,8 +77,8 @@ export interface OptionalDecorator {
*
* The following code allows the possibility of a null result:
*
* <code-example path="core/di/ts/metadata_spec.ts" region="Optional"
* linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="Optional">
* </code-example>
*
*/
(): any;
@ -119,8 +119,8 @@ export interface SelfDecorator {
* by the local injector when instantiating the class itself, but not
* when instantiating a child.
*
* <code-example path="core/di/ts/metadata_spec.ts" region="Self"
* linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="Self">
* </code-example>
*
*
* @see `SkipSelf`
@ -164,8 +164,8 @@ export interface SkipSelfDecorator {
* In the following example, the dependency can be resolved when
* instantiating a child, but not when instantiating the class itself.
*
* <code-example path="core/di/ts/metadata_spec.ts" region="SkipSelf"
* linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="SkipSelf">
* </code-example>
*
* Learn more in the
* [Dependency Injection guide](guide/dependency-injection-in-action#skip).
@ -211,8 +211,8 @@ export interface HostDecorator {
*
* The following shows use with the `@Optional` decorator, and allows for a null result.
*
* <code-example path="core/di/ts/metadata_spec.ts" region="Host"
* linenums="false"></code-example>
* <code-example path="core/di/ts/metadata_spec.ts" region="Host">
* </code-example>
*/
(): any;
new (): Host;
@ -254,11 +254,11 @@ export interface AttributeDecorator {
*
* The following example uses the decorator to inject the string literal `text`.
*
* {@example core/ts/metadata/metadata.ts region='attributeMetadata' linenums="false"}
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory' linenums="false"}
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
*/
(name: string): any;

View File

@ -69,8 +69,7 @@ export interface AttributeDecorator {
*
* A decorator can inject string literal `text` as in the following example.
*
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'
* linenums="false"}
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* @publicApi
*/
@ -143,16 +142,14 @@ export interface ContentChildrenDecorator {
*
* Here is a simple demonstration of how the `ContentChildren` decorator can be used.
*
* {@example core/di/ts/contentChildren/content_children_howto.ts region='HowTo'
* linenums="false"}
* {@example core/di/ts/contentChildren/content_children_howto.ts region='HowTo'}
*
* ### Tab-pane example
*
* Here is a slightly more realistic example that shows how `ContentChildren` decorators
* can be used to implement a tab pane component.
*
* {@example core/di/ts/contentChildren/content_children_example.ts region='Component'
* linenums="false"}
* {@example core/di/ts/contentChildren/content_children_example.ts region='Component'}
*
* @Annotation
*/
@ -211,13 +208,11 @@ export interface ContentChildDecorator {
*
* @usageNotes
*
* {@example core/di/ts/contentChild/content_child_howto.ts region='HowTo'
* linenums="false"}
* {@example core/di/ts/contentChild/content_child_howto.ts region='HowTo'}
*
* ### Example
*
* {@example core/di/ts/contentChild/content_child_example.ts region='Component'
* linenums="false"}
* {@example core/di/ts/contentChild/content_child_example.ts region='Component'}
*
* @Annotation
*/
@ -269,13 +264,11 @@ export interface ViewChildrenDecorator {
*
* @usageNotes
*
* {@example core/di/ts/viewChildren/view_children_howto.ts region='HowTo'
* linenums="false"}
* {@example core/di/ts/viewChildren/view_children_howto.ts region='HowTo'}
*
* ### Another example
*
* {@example core/di/ts/viewChildren/view_children_example.ts region='Component'
* linenums="false"}
* {@example core/di/ts/viewChildren/view_children_example.ts region='Component'}
*
* @Annotation
*/
@ -341,13 +334,11 @@ export interface ViewChildDecorator {
*
* @usageNotes
*
* {@example core/di/ts/viewChild/view_child_example.ts region='Component'
* linenums="false"}
* {@example core/di/ts/viewChild/view_child_example.ts region='Component'}
*
* ### Example 2
*
* {@example core/di/ts/viewChild/view_child_howto.ts region='HowTo'
* linenums="false"}
* {@example core/di/ts/viewChild/view_child_howto.ts region='HowTo'}
*
* @Annotation
*/

View File

@ -332,9 +332,7 @@ export interface ComponentDecorator {
* The following example creates a component with two data-bound properties,
* specified by the `inputs` value.
*
* <code-example path="core/ts/metadata/directives.ts" region="component-input"
* linenums="false">
* </code-example>
* <code-example path="core/ts/metadata/directives.ts" region="component-input"></code-example>
*
*
* ### Setting component outputs
@ -342,8 +340,7 @@ export interface ComponentDecorator {
* The following example shows two event emitters that emit on an interval. One
* emits an output every second, while the other emits every five seconds.
*
* {@example core/ts/metadata/directives.ts region='component-output-interval
* linenums="false"}
* {@example core/ts/metadata/directives.ts region='component-output-interval'}
*
* ### Injecting a class with a view provider
*

View File

@ -98,8 +98,7 @@ export class FormBuilder {
*
* The following example returns a control with an initial value in a disabled state.
*
* <code-example path="forms/ts/formBuilder/form_builder_example.ts"
* linenums="false" region="disabled-control">
* <code-example path="forms/ts/formBuilder/form_builder_example.ts" region="disabled-control">
* </code-example>
*/
control(

View File

@ -92,7 +92,7 @@ export function createEmptyStateSnapshot(
* Use to traverse the `RouterState` tree and extract information from nodes.
*
* {@example router/activated-route/module.ts region="activated-route"
* header="activated-route.component.ts" linenums="false"}
* header="activated-route.component.ts"}
*
* @publicApi
*/

View File

@ -17,7 +17,7 @@ import {UpgradeModule} from '@angular/upgrade/static';
*
* @usageNotes
*
* <code-example language="typescript" linenums="false">
* <code-example language="typescript">
* @NgModule({
* imports: [
* RouterModule.forRoot(SOME_ROUTES),

View File

@ -23,7 +23,7 @@ import {SwUpdate} from './update';
* for example via a function call:
*
* {@example service-worker/registration-options/module.ts region="registration-options"
* header="app.module.ts" linenums="false"}
* header="app.module.ts"}
*
* @publicApi
*/