docs(aio): update migrated content from anguar.io

This commit is contained in:
Peter Bacon Darwin
2017-03-27 16:08:53 +01:00
committed by Pete Bacon Darwin
parent ff82756415
commit fd72fad8fd
1901 changed files with 20145 additions and 45127 deletions

View File

@ -79,9 +79,11 @@ The following table lists some of the key AngularJS template features with their
<td>
### Bindings/interpolation
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='interpolation'}
In Angular, a template expression in curly braces still denotes one-way binding.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false">
</code-example>
In Angular, a template expression in curly braces still denotes one-way binding.
This binds the value of the element to a property of the component.
The context of the binding is implied and is always the
associated component, so it needs no reference variable.
@ -111,9 +113,11 @@ The following table lists some of the key AngularJS template features with their
<td>
### Pipes
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='uppercase'}
In Angular you use similar syntax with the pipe (|) character to filter output, but now you call them **pipes**.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false">
</code-example>
In Angular you use similar syntax with the pipe (|) character to filter output, but now you call them **pipes**.
Many (but not all) of the built-in filters from AngularJS are
built-in pipes in Angular.
@ -141,9 +145,11 @@ The following table lists some of the key AngularJS template features with their
<td>
### Input variables
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='local'}
Angular has true template input variables that are explicitly defined using the `let` keyword.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="local" linenums="false">
</code-example>
Angular has true template input variables that are explicitly defined using the `let` keyword.
For more information, see the [ngFor micro-syntax](guide/template-syntax)
section of the [Template Syntax](guide/template-syntax) page.
@ -208,13 +214,17 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Bootstrapping
{@example 'cb-ajs-quick-reference/ts/src/main.ts'}
<code-example path="cb-ajs-quick-reference/src/main.ts" linenums="false">
</code-example>
<br>
<br>
{@example 'cb-ajs-quick-reference/ts/src/app/app.module.1.ts'}
Angular doesn't have a bootstrap directive.
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example>
Angular doesn't have a bootstrap directive.
To launch the app in code, explicitly bootstrap the application's root module (`AppModule`)
in `main.ts`
and the application's root component (`AppComponent`) in `app.module.ts`.
@ -250,9 +260,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### ngClass
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='ngClass'}
In Angular, the `ngClass` directive works similarly.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false">
</code-example>
In Angular, the `ngClass` directive works similarly.
It includes/excludes CSS classes based on an expression.
In the first example, the `active` class is applied to the element if `isActive` is true.
@ -292,9 +304,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Bind to the `click` event
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='event-binding'}
AngularJS event-based directives do not exist in Angular.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false">
</code-example>
AngularJS event-based directives do not exist in Angular.
Rather, define one-way binding from the template view to the component using **event binding**.
For event binding, define the name of the target event within parenthesis and
@ -335,9 +349,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Component decorator
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.ts' region='component'}
In Angular, the template no longer specifies its associated controller.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example>
In Angular, the template no longer specifies its associated controller.
Rather, the component specifies its associated template as part of the component class decorator.
For more information, see [Architecture Overview](guide/architecture).
@ -391,9 +407,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Bind to the `href` property
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='href'}
Angular uses property binding; there is no built-in *href* directive.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="href" linenums="false">
</code-example>
Angular uses property binding; there is no built-in *href* directive.
Place the element's `href` property in square brackets and set it to a quoted template expression.
For more information see the [Property binding](guide/template-syntax)
@ -401,9 +419,11 @@ 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.
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='router-link'}
For more information on routing, see the [RouterLink binding](guide/router)
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false">
</code-example>
For more information on routing, see the [RouterLink binding](guide/router)
section of the [Routing & Navigation](guide/router) page.
</td>
@ -430,9 +450,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### *ngIf
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='ngIf'}
The `*ngIf` directive in Angular works the same as the `ng-if` directive in AngularJS. It removes
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false">
</code-example>
The `*ngIf` directive in Angular works the same as the `ng-if` directive in AngularJS. It removes
or recreates a portion of the DOM based on an expression.
In this example, the `<table>` element is removed from the DOM unless the `movies` array has a length.
@ -461,9 +483,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### ngModel
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='ngModel'}
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)
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false">
</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)
and event binding (from the view to the component), thereby providing two-way binding.
For more information on two-way binding with `ngModel`, see the [NgModel&mdash;Two-way binding to
@ -493,9 +517,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### *ngFor
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='ngFor'}
The `*ngFor` directive in Angular is similar to the `ng-repeat` directive in AngularJS. It repeats
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false">
</code-example>
The `*ngFor` directive in Angular is similar to the `ng-repeat` directive in AngularJS. It repeats
the associated DOM element for each item in the specified collection.
More accurately, it turns the defined element (`<tr>` in this example) and its contents into a template and
uses that template to instantiate a view for each item in the list.
@ -532,9 +558,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Bind to the `hidden` property
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='hidden'}
Angular uses property binding; there is no built-in *show* directive.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false">
</code-example>
Angular uses property binding; there is no built-in *show* directive.
For hiding and showing elements, bind to the HTML `hidden` property.
To conditionally display an element, place the element's `hidden` property in square brackets and
@ -567,9 +595,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### Bind to the `src` property
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='src'}
Angular uses property binding; there is no built-in *src* directive.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="src" linenums="false">
</code-example>
Angular uses property binding; there is no built-in *src* directive.
Place the `src` property in square brackets and set it to a quoted template expression.
For more information on property binding, see the [Property binding](guide/template-syntax)
@ -600,9 +630,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### ngStyle
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='ngStyle'}
In Angular, the `ngStyle` directive works similarly. It sets a CSS style on an HTML element based on an expression.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false">
</code-example>
In Angular, the `ngStyle` directive works similarly. It sets a CSS style on an HTML element based on an expression.
In the first example, the `color` style is set to the current value of the `colorPreference` variable.
@ -651,9 +683,11 @@ The following are some of the key AngularJS built-in directives and their equiva
<td>
### ngSwitch
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.html' region='ngSwitch'}
In Angular, the `ngSwitch` directive works similarly.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false">
</code-example>
In Angular, the `ngSwitch` directive works similarly.
It displays an element whose `*ngSwitchCase` matches the current `ngSwitch` expression value.
In this example, if `favoriteHero` is not set, the `ngSwitch` value is `null`
@ -726,9 +760,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### currency
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='currency'}
The Angular `currency` pipe is similar although some of the parameters have changed.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false">
</code-example>
The Angular `currency` pipe is similar although some of the parameters have changed.
</td>
@ -740,7 +776,7 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### date
<code-example>
&lt;td>{{movie.releaseDate | date}}&lt;/td>
&lt;td>{{movie.releaseDate | date}}&lt;/td>
</code-example>
Formats a date to a string based on the requested format.
@ -750,9 +786,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### date
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='date'}
The Angular `date` pipe is similar.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="date" linenums="false">
</code-example>
The Angular `date` pipe is similar.
</td>
@ -797,9 +835,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### json
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='json'}
The Angular `json` pipe does the same thing.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="json" linenums="false">
</code-example>
The Angular `json` pipe does the same thing.
</td>
@ -822,9 +862,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### slice
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='slice'}
The `SlicePipe` does the same thing but the *order of the parameters is reversed*, in keeping
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false">
</code-example>
The `SlicePipe` does the same thing but the *order of the parameters is reversed*, in keeping
with the JavaScript `Slice` method.
The first parameter is the starting index; the second is the limit.
As in AngularJS, coding this operation within the component instead could improve performance.
@ -849,9 +891,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### lowercase
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='lowercase'}
The Angular `lowercase` pipe does the same thing.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false">
</code-example>
The Angular `lowercase` pipe does the same thing.
</td>
@ -863,7 +907,7 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### number
<code-example>
&lt;td>{{movie.starRating | number}}&lt;/td>
&lt;td>{{movie.starRating | number}}&lt;/td>
</code-example>
Formats a number as text.
@ -873,9 +917,11 @@ For more information on pipes, see [Pipes](guide/pipes).
<td>
### number
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='number'}
The Angular `number` pipe is similar.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="number" linenums="false">
</code-example>
The Angular `number` pipe is similar.
It provides more functionality when defining
the decimal places, as shown in the second example above.
@ -999,9 +1045,11 @@ The Angular code is shown using TypeScript.
<td>
### Angular modules
{@example 'cb-ajs-quick-reference/ts/src/app/app.module.1.ts'}
Angular modules, defined with the `NgModule` decorator, serve the same purpose:
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example>
Angular modules, defined with the `NgModule` decorator, serve the same purpose:
- `imports`: specifies the list of other modules that this module depends upon
- `declaration`: keeps track of your components, pipes, and directives.
@ -1035,9 +1083,11 @@ The Angular code is shown using TypeScript.
<td>
### Component decorator
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.ts' region='component'}
Angular adds a decorator to the component class to provide any required metadata.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example>
Angular adds a decorator to the component class to provide any required metadata.
The `@Component` decorator declares that the class is a component and provides metadata about
that component such as its selector (or tag) and its template.
@ -1067,9 +1117,11 @@ The Angular code is shown using TypeScript.
<td>
### Component class
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.ts' region='class'}
In Angular, you create a component class.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false">
</code-example>
In Angular, you create a component class.
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
@ -1102,9 +1154,11 @@ The Angular code is shown using TypeScript.
<td>
### Dependency injection
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.ts' region='di'}
In Angular, you pass in dependencies as arguments to the component class constructor.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false">
</code-example>
In Angular, you pass in dependencies as arguments to the component class constructor.
This example injects a `MovieService`.
The first parameter's TypeScript type tells Angular what to inject, even after minification.
@ -1173,16 +1227,20 @@ also encapsulate a style sheet within a specific component.
<td>
### Link tag
{@example 'cb-ajs-quick-reference/ts/src/index.html' region='style'}
In Angular, you can continue to use the link tag to define the styles for your application in the `index.html` file.
<code-example path="cb-ajs-quick-reference/src/index.html" region="style" linenums="false">
</code-example>
In Angular, you can continue to use the link tag to define the styles for your application in the `index.html` file.
But now you can also encapsulate styles for your components. ### StyleUrls
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
a style sheet for a particular component.
{@example 'cb-ajs-quick-reference/ts/src/app/movie-list.component.ts' region='style-url'}
This allows you to set appropriate styles for individual components that wont leak into
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false">
</code-example>
This allows you to set appropriate styles for individual components that wont leak into
other parts of the application.
</td>

View File

@ -41,9 +41,16 @@ add it to your page.
* [Parallel animation groups](guide/animations#parallel-animation-groups).
* [Animation callbacks](guide/animations#animation-callbacks).
~~~ {.l-sub-section}
The examples in this page are available as a <live-example></live-example>.
~~~
{@a example-transitioning-between-states}
## Quickstart example: Transitioning between two states
@ -55,17 +62,27 @@ You can build a simple animation that transitions an element between two states
driven by a model attribute.
Animations are defined inside `@Component` metadata. Before you can add animations, you need
to import a few animation-specific functions:
to import a few animation-specific imports and functions:
{@example 'animations/ts/src/app/hero-list-basic.component.ts' region='imports'}
<code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false">
</code-example>
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false">
</code-example>
With these, you can define an *animation trigger* called `heroState` in the component
metadata. It uses animations to transition between two states: `active` and `inactive`. When a
hero is active, the element appears in a slightly larger size and lighter color.
{@example 'animations/ts/src/app/hero-list-basic.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false">
</code-example>
@ -81,7 +98,9 @@ Now, using the `[@triggerName]` syntax, attach the animation that you just defin
one or more elements in the component's template.
{@example 'animations/ts/src/app/hero-list-basic.component.ts' region='template'}
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false">
</code-example>
Here, the animation trigger applies to every element repeated by an `ngFor`. Each of
the repeated elements animates independently. The value of the
@ -91,7 +110,9 @@ With this setup, an animated transition appears whenever a hero object changes s
Here's the full component implementation:
{@example 'animations/ts/src/app/hero-list-basic.component.ts'}
<code-example path="animations/src/app/hero-list-basic.component.ts">
</code-example>
## States and transitions
@ -107,7 +128,9 @@ component's template.
You can define *styles* for each animation state:
{@example 'animations/ts/src/app/hero-list-basic.component.ts' region='states'}
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false">
</code-example>
These `state` definitions specify the *end styles* of each state.
They are applied to the element once it has transitioned to that state, and stay
@ -117,7 +140,9 @@ After you define states, you can define *transitions* between the states. Each t
controls the timing of switching between one set of styles and the next:
{@example 'animations/ts/src/app/hero-list-basic.component.ts' region='transitions'}
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false">
</code-example>
<figure class='image-display'>
@ -128,13 +153,17 @@ If several transitions have the same timing configuration, you can combine
them into the same `transition` definition:
{@example 'animations/ts/src/app/hero-list-combined-transitions.component.ts' region='transitions'}
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false">
</code-example>
When both directions of a transition have the same timing, as in the previous
example, you can use the shorthand syntax `<=>`:
{@example 'animations/ts/src/app/hero-list-twoway.component.ts' region='transitions'}
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false">
</code-example>
You can also apply a style during an animation but not keep it around
after the animation finishes. You can define such styles inline, in the `transition`. In this example,
@ -143,7 +172,9 @@ When the transition finishes, none of these styles are kept because they're not
defined in a `state`.
{@example 'animations/ts/src/app/hero-list-inline-styles.component.ts' region='transitions'}
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false">
</code-example>
### The wildcard state `*`
@ -187,13 +218,18 @@ entering and leaving of elements:
For example, in the `animations` !{_array} below there are two transitions that use
the `void => *` and `* => void` syntax to animate the element in and out of the view.
{@example 'animations/ts/src/app/hero-list-enter-leave.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false">
</code-example>
Note that in this case the styles are applied to the void state directly in the
transition definitions, and not in a separate `state(void)` definition. Thus, the transforms
are different on enter and leave: the element enters from the left
and leaves to the right.
~~~ {.l-sub-section}
These two common animations have their own aliases:
<code-example language="typescript">
transition(':enter', [ ... ]); // void => *
@ -201,6 +237,10 @@ These two common animations have their own aliases:
</code-example>
~~~
## Example: Entering and leaving from different states
<figure>
<img src="assets/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"> </img>
@ -224,7 +264,9 @@ This gives you fine-grained control over each transition:
{@example 'animations/ts/src/app/hero-list-enter-leave-states.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false">
</code-example>
## Animatable properties and units
@ -262,7 +304,9 @@ In this example, the leave animation takes whatever height the element has befor
leaves and animates from that height to zero:
{@example 'animations/ts/src/app/hero-list-auto.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false">
</code-example>
## Animation timing
@ -310,7 +354,9 @@ slight delay of 10 milliseconds as specified in `'0.2s 10 ease-out'`:
{@example 'animations/ts/src/app/hero-list-timings.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false">
</code-example>
## Multi-step animations with keyframes
<figure>
@ -328,7 +374,9 @@ This example adds some "bounce" to the enter and leave animations with
keyframes:
{@example 'animations/ts/src/app/hero-list-multistep.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false">
</code-example>
Note that the offsets are *not* defined in terms of absolute time. They are relative
measures from zero to one. The final timeline of the animation is based on the combination
@ -354,7 +402,9 @@ enter and leave allows for two different timing configurations. Both
are applied to the same element in parallel, but run independently of each other:
{@example 'animations/ts/src/app/hero-list-groups.component.ts' region='animationdef'}
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false">
</code-example>
One group animates the element transform and width; the other group animates the opacity.
## Animation callbacks
@ -365,9 +415,11 @@ In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
those callbacks like this:
{@example 'animations/ts/src/app/hero-list-multistep.component.ts' region='template'}
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false">
The callbacks receive an `AnimationTransitionEvent` which contains useful properties such as `fromState`,
`toState` and `totalTime`.
</code-example>
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
`fromState`, `toState` and `totalTime`.
Those callbacks will fire whether or not an animation is picked up.

View File

@ -2,37 +2,54 @@
Ahead-of-Time Compilation
@intro
Learn how to use Ahead-of-time compilation.
Learn how to use ahead-of-time compilation.
@description
This cookbook describes how to radically improve performance by compiling _Ahead of Time_ (AOT)
This cookbook describes how to radically improve performance by compiling _ahead-of-time_ (AOT)
during a build process.
{@a toc}
## Table of Contents
* [Overview](guide/aot-compiler#overview)
* [_Ahead-of-Time_ vs _Just-in-Time_](guide/aot-compiler#aot-jit)
* [Compile with AOT](guide/aot-compiler#compile)
* [Bootstrap](guide/aot-compiler#bootstrap)
* [Tree Shaking](guide/aot-compiler#tree-shaking)
* [Load the bundle](guide/aot-compiler#load)
* [Serve the app](guide/aot-compiler#serve)
* [Workflow and convenience script](guide/aot-compiler#workflow)
* [Source Code](guide/aot-compiler#source-code)
* [Tour of Heroes](guide/aot-compiler#toh)
# Contents
- [Overview](guide/overview)
- [Ahead-of-time (AOT) vs just-in-time (JIT)](guide/aot-compiler#aot-jit)
- [Why do AOT compilation?](guide/aot-compiler#why-aot)
- [Compile with AOT](guide/aot-compiler#compile)
- [Bootstrap](guide/aot-compiler#bootstrap)
- [Tree shaking](guide/aot-compiler#tree-shaking)
- [Rollup](guide/aot-compiler#rollup)
- [Rollup Plugins](guide/aot-compiler#rollup-plugins)
- [Run Rollup](guide/aot-compiler#run-rollup)
- [Load the bundle](guide/aot-compiler#load)
- [Serve the app](guide/aot-compiler#serve)
- [AOT QuickStart source code](guide/aot-compiler#source-code)
- [Workflow and convenience script](guide/aot-compiler#workflow)
- [Develop JIT along with AOT](guide/aot-compiler#run-jit)
- [Tour of Heroes](guide/aot-compiler#toh)
- [JIT in development, AOT in production](guide/aot-compiler#jit-dev-aot-prod)
- [Tree shaking](guide/aot-compiler#shaking)
- [Running the application](guide/aot-compiler#running-app)
- [Inspect the Bundle](guide/aot-compiler#inspect-bundle)
{@a overview}
## Overview
An Angular application consist largely of components and their HTML templates.
An Angular application consists largely of components and their HTML templates.
Before the browser can render the application,
the components and templates must be converted to executable JavaScript by the _Angular compiler_.
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo" target="_blank">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.You can compile the app in the browser, at runtime, as the application loads, using the **_Just-in-Time_ (JIT) compiler**.
~~~ {.l-sub-section}
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo" target="_blank">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.
~~~
You can compile the app in the browser, at runtime, as the application loads, using the **_just-in-time_ (JIT) compiler**.
This is the standard development approach shown throughout the documentation.
It's great .. but it has shortcomings.
It's great but it has shortcomings.
JIT compilation incurs a runtime performance penalty.
Views take longer to render because of the in-browser compilation step.
@ -41,22 +58,23 @@ and a lot of library code that the application won't actually need.
Bigger apps take longer to transmit and are slower to load.
Compilation can uncover many component-template binding errors.
JIT compilation discovers them at runtime which is later than we'd like.
JIT compilation discovers them at runtime, which is late in the process.
The **_Ahead-of-Time_ (AOT) compiler** can catch template errors early and improve performance
by compiling at build time as you'll learn in this chapter.
The **_ahead-of-time_ (AOT) compiler** can catch template errors early and improve performance
by compiling at build time.
{@a aot-jit}
## _Ahead-of-time_ (AOT) vs _Just-in-time_ (JIT)
## _Ahead-of-time_ (AOT) vs _just-in-time_ (JIT)
There is actually only one Angular compiler. The difference between AOT and JIT is a matter of timing and tooling.
With AOT, the compiler runs once at build time using one set of libraries;
With JIT it runs every time for every user at runtime using a different set of libraries.
with JIT it runs every time for every user at runtime using a different set of libraries.
### Why do AOT compilation?
{@a why-aot}
## Why do AOT compilation?
*Faster rendering*
@ -65,7 +83,7 @@ The browser loads executable code so it can render the application immediately,
*Fewer asynchronous requests*
The compiler _inlines_ external html templates and css style sheets within the application JavaScript,
The compiler _inlines_ external HTML templates and CSS style sheets within the application JavaScript,
eliminating separate ajax requests for those source files.
*Smaller Angular framework download size*
@ -91,23 +109,24 @@ there are fewer opportunities for injection attacks.
## Compile with AOT
### Prepare for offline compilation
Preparing for offline compilation takes a few simple steps.
Take the <a href='../guide/setup.html'>Setup</a> as a starting point.
A few minor changes to the lone `app.component` lead to these two class and html files:
<md-tab-group>
<md-tab label="src/app/app.component.html">
{@example 'cb-aot-compiler/ts/src/app/app.component.html'}
</md-tab>
A few minor changes to the lone `app.component` lead to these two class and HTML files:
<md-tab label="src/app/app.component.ts">
{@example 'cb-aot-compiler/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane>
</md-tab-group>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane>
</code-tabs>
Install a few new npm dependencies with the following command:
<code-example language="none" class="code-shell">
@ -120,39 +139,32 @@ instead of the TypeScript compiler (`tsc`).
`ngc` is a drop-in replacement for `tsc` and is configured much the same way.
`ngc` requires its own `tsconfig.json` with AOT-oriented settings.
Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` (on the project root),
then modify it to look as follows.
Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` on the project root,
then modify it as follows.
{@example 'cb-aot-compiler/ts/tsconfig-aot.json'}
<code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false">
</code-example>
The `compilerOptions` section is unchanged except for one property.
**Set the `module` to `es2015`**.
This is important as explained later in the [Tree Shaking](guide/aot-compiler#tree-shaking) section.
What's really new is the `ngc` section at the bottom called `angularCompilerOptions`.
Its `"genDir"` property tells the compiler
Its `genDir` property tells the compiler
to store the compiled output files in a new `aot` folder.
The `"skipMetadataEmit" : true` property prevents the compiler from generating metadata files with the compiled application.
Metadata files are not necessary when targeting TypeScript files, so there is no reason to include them.
***Component-relative Template URLS***
***Component-relative template URLS***
The AOT compiler requires that `@Component` URLS for external templates and css files be _component-relative_.
The AOT compiler requires that `@Component` URLS for external templates and CSS files be _component-relative_.
That means that the value of `@Component.templateUrl` is a URL value _relative_ to the component class file.
For example, an `'app.component.html'` URL means that the template file is a sibling of its companion `app.component.ts` file.
While JIT app URLs are more flexible, stick with _component-relative_ URLs for compatibility with AOT compilation.
JIT-compiled applications that use the SystemJS loader and _component-relative_ URLs *must set the* `@Component.moduleId` *property to* `module.id`.
The `module` object is undefined when an AOT-compiled app runs.
The app fails with a null reference error unless you assign a global `module` value in the `index.html` like this:
{@example 'cb-aot-compiler/ts/src/index.html' region='moduleId'}
Setting a global `module` is a temporary expedient.
### Compiling the application
***Compiling the application***
Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing:
<code-example language="none" class="code-shell">
@ -160,26 +172,41 @@ Initiate AOT compilation from the command line using the previously installed `n
</code-example>
~~~ {.l-sub-section}
Windows users should surround the `ngc` command in double quotes:
<code-example format='.'>
"node_modules/.bin/ngc" -p tsconfig-aot.json
</code-example>
~~~
`ngc` expects the `-p` switch to point to a `tsconfig.json` file or a folder containing a `tsconfig.json` file.
After `ngc` completes, look for a collection of _NgFactory_ files in the `aot` folder (the folder specified as `genDir` in `tsconfig-aot.json`).
After `ngc` completes, look for a collection of _NgFactory_ files in the `aot` folder.
The `aot` folder is the directory specified as `genDir` in `tsconfig-aot.json`.
These factory files are essential to the compiled application.
Each component factory creates an instance of the component at runtime by combining the original class file
and a JavaScript representation of the component's template.
Note that the original component class is still referenced internally by the generated factory.
The curious can open the `aot/app.component.ngfactory.ts` to see the original Angular template syntax
in its intermediate, compiled-to-TypeScript form.
~~~ {.l-sub-section}
The curious can open `aot/app.component.ngfactory.ts` to see the original Angular template syntax
compiled to TypeScript, its intermediate form.
JIT compilation generates these same _NgFactories_ in memory where they are largely invisible.
AOT compilation reveals them as separate, physical files.
~~~
~~~ {.alert.is-important}
Do not edit the _NgFactories_! Re-compilation replaces these files and all edits will be lost.
@ -193,7 +220,7 @@ Do not edit the _NgFactories_! Re-compilation replaces these files and all edits
## Bootstrap
The AOT path changes application bootstrapping.
The AOT approach changes application bootstrapping.
Instead of bootstrapping `AppModule`, you bootstrap the application with the generated module factory, `AppModuleNgFactory`.
@ -206,54 +233,64 @@ Switch from the `platformBrowserDynamic.bootstrap` used in JIT compilation to
Here is AOT bootstrap in `main.ts` next to the original JIT version:
<md-tab-group>
<md-tab label="src/main.ts">
{@example 'cb-aot-compiler/ts/src/main.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane>
<md-tab label="src/main-jit.ts">
{@example 'cb-aot-compiler/ts/src/main-jit.ts'}
</md-tab>
<code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Be sure to recompile with `ngc`!
Be sure to [recompile](guide/aot-compiler#compiling-aot) with `ngc`!
{@a tree-shaking}
## Tree Shaking
## Tree shaking
AOT compilation sets the stage for further optimization through a process called _Tree Shaking_.
A Tree Shaker walks the dependency graph, top to bottom, and _shakes out_ unused code like
dead needles in a Christmas tree.
AOT compilation sets the stage for further optimization through a process called _tree shaking_.
A tree shaker walks the dependency graph, top to bottom, and _shakes out_ unused code like
dead leaves in a tree.
Tree Shaking can greatly reduce the downloaded size of the application
Tree shaking can greatly reduce the downloaded size of the application
by removing unused portions of both source and library code.
In fact, most of the reduction in small apps comes from removing unreferenced Angular features.
For example, this demo application doesn't use anything from the `@angular/forms` library.
There is no reason to download Forms-related Angular code and tree shaking ensures that you don't.
There is no reason to download forms-related Angular code and tree shaking ensures that you don't.
Tree Shaking and AOT compilation are separate steps.
Tree Shaking can only target JavaScript code.
Tree shaking and AOT compilation are separate steps.
Tree shaking can only target JavaScript code.
AOT compilation converts more of the application to JavaScript,
which in turn makes more of the application "Tree Shakable".
which in turn makes more of the application "tree shakable".
{@a rollup}
### Rollup
This cookbook illustrates a Tree Shaking utility called _Rollup_.
This cookbook illustrates a tree shaking utility called _Rollup_.
Rollup statically analyzes the application by following the trail of `import` and `export` statements.
It produces a final code _bundle_ that excludes code that is exported, but never imported.
Rollup can only Tree Shake `ES2015` modules which have `import` and `export` statements.
Rollup can only tree shake `ES2015` modules which have `import` and `export` statements.
~~~ {.l-sub-section}
Recall that `tsconfig-aot.json` is configured to produce `ES2015` modules.
It's not important that the code itself be written with `ES2015` syntax such as `class` and `const`.
What matters is that the code uses ES `import` and `export` statements rather than `require` statements.Install the Rollup dependencies with this command:
<code-example format='.'>
What matters is that the code uses ES `import` and `export` statements rather than `require` statements.
~~~
In the terminal window, install the Rollup dependencies with this command:
<code-example language="none" class="code-shell">
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
</code-example>
@ -262,66 +299,98 @@ in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this.
{@example 'cb-aot-compiler/ts/rollup-config.js'}
<code-example path="cb-aot-compiler/rollup-config.js" linenums="false">
It tells Rollup that the app entry point is `src/app/main.js` .
</code-example>
This config file tells Rollup that the app entry point is `src/app/main.js` .
The `dest` attribute tells Rollup to create a bundle called `build.js` in the `dist` folder.
It overrides the default `onwarn` method in order to skip annoying messages about the AOT compiler's use of the `this` keyword.
Then there are plugins.
The next section covers the plugins in more depth.
{@a rollup-plugins}
### Rollup Plugins
Optional plugins filter and transform the Rollup inputs and output.
*RxJS*
Rollup expects application source code to use `ES2015` modules.
Not all external dependencies are published as `ES2015` modules.
In fact, most are not. Many of them are published as _CommonJS_ modules.
The _RxJs_ observable library is an essential Angular dependency published as an ES5 JavaScript _CommonJS_ module.
The _RxJs_ Observable library is an essential Angular dependency published as an ES5 JavaScript _CommonJS_ module.
Luckily there is a Rollup plugin that modifies _RxJs_
Luckily, there is a Rollup plugin that modifies _RxJs_
to use the ES `import` and `export` statements that Rollup requires.
Rollup then preserves in the final bundle the parts of `RxJS` referenced by the application.
Rollup then preserves the parts of `RxJS` referenced by the application
in the final bundle. Using it is straigthforward. Add the following to
the `plugins` !{_array} in `rollup-config.js`:
{@example 'cb-aot-compiler/ts/rollup-config.js' region='commonjs'}
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false">
</code-example>
*Minification*
Rollup Tree Shaking reduces code size considerably. Minification makes it smaller still.
This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code.
Rollup tree shaking reduces code size considerably. Minification makes it smaller still.
This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code.
Add the following to the `plugins` !{_array}:
{@example 'cb-aot-compiler/ts/rollup-config.js' region='uglify'}
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false">
</code-example>
~~~ {.l-sub-section}
In a production setting, you would also enable gzip on the web server to compress
the code into an even smaller package going over the wire.
~~~
{@a run-rollup}
### Run Rollup
Execute the Rollup process with this command:
<code-example format='.'>
<code-example language="none" class="code-shell">
node_modules/.bin/rollup -c rollup-config.js
</code-example>
~~~ {.l-sub-section}
Windows users should surround the `rollup` command in double quotes:
<code-example format='.'>
<code-example language="none" class="code-shell">
"node_modules/.bin/rollup" -c rollup-config.js
</code-example>
~~~
{@a load}
## Load the Bundle
## Load the bundle
Loading the generated application bundle does not require a module loader like SystemJS.
Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single `script` tag **_after_** the `</body>` tag:
Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag:
{@example 'cb-aot-compiler/ts/src/index.html' region='bundle'}
<code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false">
</code-example>
@ -330,7 +399,7 @@ Instead, load the bundle file using a single `script` tag **_after_** the `</bod
## Serve the app
You'll need a web server to host the application.
Use the same _Lite Server_ employed elsewhere in the documentation:
Use the same `lite-server` employed elsewhere in the documentation:
<code-example language="none" class="code-shell">
npm run lite
</code-example>
@ -340,42 +409,43 @@ The server starts, launches a browser, and the app should appear.
{@a source-code}
## AOT QuickStart Source Code
## AOT QuickStart source code
Here's the pertinent source code:
<md-tab-group>
<md-tab label="src/app/app.component.html">
{@example 'cb-aot-compiler/ts/src/app/app.component.html'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane>
<md-tab label="src/app/app.component.ts">
{@example 'cb-aot-compiler/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane>
<md-tab label="src/main.ts">
{@example 'cb-aot-compiler/ts/src/main.ts'}
</md-tab>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane>
<md-tab label="src/index.html">
{@example 'cb-aot-compiler/ts/src/index.html'}
</md-tab>
<code-pane title="src/index.html" path="cb-aot-compiler/src/index.html">
</code-pane>
<md-tab label="tsconfig-aot.json">
{@example 'cb-aot-compiler/ts/tsconfig-aot.json'}
</md-tab>
<code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json">
</code-pane>
<md-tab label="rollup-config.js">
{@example 'cb-aot-compiler/ts/rollup-config.js'}
</md-tab>
<code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js">
</code-pane>
</md-tab-group>
</code-tabs>
@ -395,7 +465,7 @@ Add the following _npm_ convenience script to the `package.json` so you can comp
{@a run-jit}
### And JIT too!
### Develop JIT along with AOT
AOT compilation and rollup together take several seconds.
You may be able to develop iteratively a little faster with SystemJS and JIT.
@ -405,24 +475,28 @@ The same source code can be built both ways. Here's one way to do that.
* Delete the script at the bottom of `index-jit.html` that loads `bundle.js`
* Restore the SystemJS scripts like this:
{@example 'cb-aot-compiler/ts/src/index-jit.html' region='jit'}
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false">
</code-example>
Notice the slight change to the `system.import` which now specifies `src/app/main-jit`.
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap)
Open a _different_ terminal window and enter.
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap).
Open a _different_ terminal window and enter `npm start`.
<code-example language="none" class="code-shell">
npm start
</code-example>
That compiles the app with JIT and launches the server.
The server loads `index.html` which is still the AOT version (confirm in the browser console).
Change the address bar to `index-jit.html` and it loads the JIT version (confirm in the browser console).
The server loads `index.html` which is still the AOT version, which you can confirm in the browser console.
Change the address bar to `index-jit.html` and it loads the JIT version.
This is also evident in the browser console.
Develop as usual.
The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser.
To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`.
When it finishes, go back to the browser and back-button to the AOT version in the (default) `index.html`.
When it finishes, go back to the browser and use the back button to
return to the AOT version in the default `index.html`.
Now you can develop JIT and AOT, side-by-side.
@ -432,13 +506,15 @@ Now you can develop JIT and AOT, side-by-side.
## Tour of Heroes
The sample above is a trivial variation of the QuickStart app.
In this section you apply what you've learned about AOT compilation and Tree Shaking
to an app with more substance, the tutorial [_Tour of Heroes_](tutorial/toh-pt6).
The sample above is a trivial variation of the QuickStart application.
In this section you apply what you've learned about AOT compilation and tree shaking
to an app with more substance, the [_Tour of Heroes_](tutorial/toh-pt6) application.
{@a jit-dev-aot-prod}
### JIT in development, AOT in production
Today AOT compilation and Tree Shaking take more time than is practical for development. That will change soon.
Today AOT compilation and tree shaking take more time than is practical for development. That will change soon.
For now, it's best to JIT compile in development and switch to AOT compilation before deploying to production.
Fortunately, the source code can be compiled either way without change _if_ you account for a few key differences.
@ -449,19 +525,20 @@ The JIT and AOT apps require their own `index.html` files because they setup and
Here they are for comparison:
<md-tab-group>
<md-tab label="aot/index.html (AOT)">
{@example 'toh-6/ts/aot/index.html'}
</md-tab>
<code-tabs>
<code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html">
</code-pane>
<md-tab label="src/index.html (JIT)">
{@example 'toh-6/ts/src/index.html'}
</md-tab>
<code-pane title="src/index.html (JIT)" path="toh-6/src/index.html">
</code-pane>
</md-tab-group>
</code-tabs>
The JIT version relies on `SystemJS` to load individual modules.
Its scripts appear in its `index.html`.
@ -475,41 +552,43 @@ JIT and AOT applications boot in much the same way but require different Angular
The key differences, covered in the [Bootstrap](guide/aot-compiler#bootstrap) section above,
are evident in these `main` files which can and should reside in the same folder:
<md-tab-group>
<md-tab label="main-aot.ts (AOT)">
{@example 'toh-6/ts/src/main-aot.ts'}
</md-tab>
<code-tabs>
<code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts">
</code-pane>
<md-tab label="main.ts (JIT)">
{@example 'toh-6/ts/src/main.ts'}
</md-tab>
<code-pane title="main.ts (JIT)" path="toh-6/src/main.ts">
</code-pane>
</md-tab-group>
</code-tabs>
***TypeScript configuration***
JIT-compiled applications transpile to `commonjs` modules.
AOT-compiled applications transpile to _ES2015_/_ES6_ modules to facilitate Tree Shaking.
AOT-compiled applications transpile to _ES2015_/_ES6_ modules to facilitate tree shaking.
AOT requires its own TypeScript configuration settings as well.
You'll need separate TypeScript configuration files such as these:
<md-tab-group>
<md-tab label="tsconfig-aot.json (AOT)">
{@example 'toh-6/ts/tsconfig-aot.json'}
</md-tab>
<code-tabs>
<code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json">
</code-pane>
<md-tab label="src/tsconfig.json (JIT)">
{@example 'toh-6/ts/src/tsconfig.1.json'}
</md-tab>
<code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json">
</code-pane>
</md-tab-group>
</code-tabs>
@ -528,16 +607,23 @@ In a more typical project, `node_modules` would be a sibling of `tsconfig-aot.js
and `"typeRoots"` would be set to `"node_modules/@types/"`.
Edit your `tsconfig-aot.json` to fit your project's file structure.
~~~
### Tree Shaking
Rollup does the Tree Shaking as before.
{@example 'toh-6/ts/rollup-config.js'}
{@a shaking}
### Tree shaking
Rollup does the tree shaking as before.
<code-example path="toh-6/rollup-config.js" linenums="false">
</code-example>
{@a running-app}
### Running the application
@ -556,29 +642,30 @@ The _Tour of Heroes_ source code is in the `public/docs/_examples/toh-6/ts` fold
Run the JIT-compiled app with `npm start` as for all other JIT examples.
Compiling with AOT presupposes certain supporting files, most of them discussed above.
<md-tab-group>
<md-tab label="src/index.html">
{@example 'toh-6/ts/src/index.html'}
</md-tab>
<code-tabs>
<code-pane title="src/index.html" path="toh-6/src/index.html">
</code-pane>
<md-tab label="copy-dist-files.js">
{@example 'toh-6/ts/copy-dist-files.js'}
</md-tab>
<code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js">
</code-pane>
<md-tab label="rollup-config.js">
{@example 'toh-6/ts/rollup-config.js'}
</md-tab>
<code-pane title="rollup-config.js" path="toh-6/rollup-config.js">
</code-pane>
<md-tab label="tsconfig-aot.json">
{@example 'toh-6/ts/tsconfig-aot.json'}
</md-tab>
<code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json">
</code-pane>
</md-tab-group>
</code-tabs>
Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script:
<code-example language="none" class="code-shell">
@ -586,12 +673,22 @@ Extend the `scripts` section of the `package.json` with these npm scripts:Copy t
</code-example>
You won't do that again until there are updates to `zone.js` or the `core-js` shim for old browsers.Now AOT-compile the app and launch it with the `lite` server:
~~~ {.l-sub-section}
You won't do that again until there are updates to `zone.js` or the `core-js` shim for old browsers.
~~~
Now AOT-compile the app and launch it with the `lite-server`:
<code-example language="none" class="code-shell">
npm run build:aot && npm run serve:aot
</code-example>
{@a inspect-bundle}
### Inspect the Bundle
It's fascinating to see what the generated JavaScript bundle looks like after Rollup.

View File

@ -14,7 +14,9 @@ The [setup](guide/setup) instructions produce a new project with the following m
You'll evolve this module as your application grows.
{@example 'setup/ts/src/app/app.module.ts'}
<code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example>
After the `import` statements, you come to a class adorned with the
**`@NgModule`** [_decorator_](guide/glossary).
@ -54,6 +56,9 @@ Other guide and cookbook pages will tell you when you need to add additional mod
~~~
~~~ {.l-sub-section}
The `import` statements at the top of the file and the Angular module's `imports` array
are unrelated and have completely different jobs.
@ -66,6 +71,10 @@ The _module's_ `imports` array appears _exclusively_ in the `@NgModule` metadata
It tells Angular about specific _other_ Angular modules &mdash; all of them classes decorated with `@NgModule` &mdash;
that the application needs to function properly.
~~~
{@a declarations}
### The _declarations_ array
@ -125,7 +134,9 @@ and you'll run it in a browser. You can learn about other options later.
The recommended place to bootstrap a JIT-compiled browser application is in a separate file
in the `src` folder named `src/main.ts`
{@example 'setup/ts/src/main.ts'}
<code-example path="setup/src/main.ts" linenums="false">
</code-example>
This code creates a browser platform for dynamic (JIT) compilation and
bootstraps the `AppModule` described above.
@ -137,7 +148,9 @@ creates an instance of the component and inserts it within the element tag ident
The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app`
so Angular looks for a `<my-app>` tag in the `index.html` like this one ...
{@example 'setup/ts/src/index.html' region='my-app'}
<code-example path="setup/src/index.html" region="my-app" linenums="false">
</code-example>
... and displays the `AppComponent` there.

View File

@ -5,6 +5,12 @@ Architecture Overview
The basic building blocks of Angular applications.
@description
Angular is a framework for building client applications in HTML and
either JavaScript or a language like TypeScript that compiles to JavaScript.
The framework consists of several libraries, some of them core and some optional.
You write Angular applications by composing HTML *templates* with Angularized markup,
writing *component* classes to manage those templates, adding application logic in *services*,
and boxing components and services in *modules*.
@ -34,16 +40,115 @@ The architecture diagram identifies the eight main building blocks of an Angular
Learn these building blocks, and you're on your way.
~~~ {.l-sub-section}
<p>
The code referenced on this page is available as a <live-example></live-example>.
</p>
~~~
## Modules
<figure>
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
</figure>
Angular apps are modular and Angular has its own modularity system called _Angular modules_ or _NgModules_.
_Angular modules_ are a big deal.
This page introduces modules; the [Angular modules](guide/ngmodule) page covers them in depth.
<br class="l-clear-both"><br>Every Angular app has at least one Angular module class, [the _root module_](guide/appmodule),
conventionally named `AppModule`.
While the _root module_ may be the only module in a small application, most apps have many more
_feature modules_, each a cohesive block of code dedicated to an application domain,
a workflow, or a closely related set of capabilities.
An Angular module, whether a _root_ or _feature_, is a class with an `@NgModule` decorator.
~~~ {.l-sub-section}
Decorators are functions that modify JavaScript classes.
Angular has many decorators that attach metadata to classes so that it knows
what those classes mean and how they should work.
<a href="https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.x5c2ndtx0" target="_blank">
Learn more</a> about decorators on the web.
~~~
`NgModule` is a decorator function that takes a single metadata object whose properties describe the module.
The most important properties are:
* `declarations` - the _view classes_ that belong to this module.
Angular has three kinds of view classes: [components](guide/architecture#components), [directives](guide/architecture#directives), and [pipes](guide/pipes).
* `exports` - the subset of declarations that should be visible and usable in the component [templates](guide/architecture#templates) of other modules.
* `imports` - other modules whose exported classes are needed by component templates declared in _this_ module.
* `providers` - creators of [services](guide/architecture#services) that this module contributes to
the global collection of services; they become accessible in all parts of the app.
* `bootstrap` - the main application view, called the _root component_,
that hosts all other app views. Only the _root module_ should set this `bootstrap` property.
Here's a simple root module:
<code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false">
</code-example>
~~~ {.l-sub-section}
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root module has no reason to _export_ anything because other components don't need to _import_ the root module.
~~~
Launch an application by _bootstrapping_ its root module.
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
<code-example path="architecture/src/main.ts" linenums="false">
</code-example>
### Angular modules vs. JavaScript modules
The Angular module &mdash; a class decorated with `@NgModule` &mdash; is a fundamental feature of Angular.
JavaScript also has its own module system for managing collections of JavaScript objects.
It's completely different and unrelated to the Angular module system.
In JavaScript each _file_ is a module and all objects defined in the file belong to that module.
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="export" linenums="false">
</code-example>
~~~ {.l-sub-section}
<a href="http://exploringjs.com/es6/ch_modules.html" target="_blank">Learn more about the JavaScript module system on the web.</a>
~~~
These are two different and _complementary_ module systems. Use them both to write your apps.
### Angular libraries
<figure>
@ -51,6 +156,45 @@ Learn these building blocks, and you're on your way.
</figure>
Angular ships as a collection of JavaScript modules. You can think of them as library modules.
Each Angular library name begins with the `!{_at_angular}` prefix.
You install them with the **npm** package manager and import parts of them with JavaScript `import` statements.
<br class="l-clear-both"><br>
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>
You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false">
</code-example>
In the example of the simple root module above, the application module needs material from within that `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>
In this way you're using both the Angular and JavaScript module systems _together_.
It's easy to confuse the two systems because they share the common vocabulary of "imports" and "exports".
Hang in there. The confusion yields to clarity with time and experience.
~~~ {.l-sub-section}
Learn more from the [Angular modules](guide/ngmodule) page.
~~~
<div class='l-hr'>
</div>
@ -77,6 +221,12 @@ The class interacts with the view through an API of properties and methods.
For example, this `HeroListComponent` has a `heroes` property that returns !{_an} !{_array} of heroes
that it acquires from a service.
`HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="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()` declared above.
@ -97,7 +247,9 @@ A template looks like regular HTML, except for a few differences. Here is a
template for our `HeroListComponent`:
{@example 'architecture/ts/src/app/hero-list.component.html'}
<code-example path="architecture/src/app/hero-list.component.html">
</code-example>
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>` uses Angular's [template syntax](guide/template-syntax).
@ -135,16 +287,26 @@ To tell Angular that `HeroListComponent` is a component, attach **metadata** to
In !{_Lang}, you attach metadata by using !{_a} **!{_decorator}**.
Here's some metadata for `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata">
</code-example>
Here is the `@Component` !{_decorator}, which identifies the class
immediately below it as a component class.
<ul if-docs="ts"><li>`moduleId`: sets the source of the base address (`module.id`) for module-relative URLs such as the `templateUrl`.</ul>
The `@Component` decorator takes a required configuration object with the
information Angular needs to create and present the component and its view.
Here are a few of the most useful `@Component` configuration options:
- `selector`: CSS selector that tells Angular to create and insert an instance of this component
where it finds a `<hero-list>` tag in *parent* HTML.
For example, if an app's HTML contains `<hero-list></hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags.
- `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
- `providers`: !{_array} of **dependency injection providers** for services that the component requires.
This is one way to tell Angular that the component's constructor requires a `HeroService`
so it can get the list of heroes to display.
@ -179,6 +341,12 @@ a mechanism for coordinating parts of a template with parts of a component.
Add binding markup to the template HTML to tell Angular how to connect both sides.
As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding">
</code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data)
displays the component's `hero.name` property value within the `<li>` element.
@ -190,6 +358,12 @@ the parent `HeroListComponent` to the `hero` property of the child `HeroDetailCo
**Two-way data binding** is an important fourth form
that combines property and event binding in a single notation, using the `ngModel` directive.
Here's an example from the `HeroDetailComponent` template:
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="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,
as with event binding.
@ -226,8 +400,15 @@ A component is a *directive-with-a-template*;
a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features.
<br class="l-clear-both">
~~~ {.l-sub-section}
While **a component is technically a directive**,
components are so distinctive and central to Angular applications that this architectural overview separates components from directives.Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
components are so distinctive and central to Angular applications that this architectural overview separates components from directives.
~~~
Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
They tend to appear within an element tag as attributes do,
sometimes by name but more often as the target of an assignment or a binding.
@ -235,8 +416,15 @@ sometimes by name but more often as the target of an assignment or a binding.
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
The [example template](guide/architecture#templates) uses two built-in structural directives:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural">
</code-example>
* [`*ngFor`](guide/displaying-data) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data) includes the `HeroDetail` component only if a selected hero exists.
**Attribute** directives alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name.
@ -244,6 +432,12 @@ 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 an `<input>`)
by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example>
Angular has a few more directives that either alter the layout structure
(for example, [ngSwitch](guide/template-syntax))
or modify aspects of DOM elements and components
@ -279,8 +473,20 @@ There is no service base class, and no place to register a service.
Yet services are fundamental to any Angular application. Components are big consumers of services.
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" title="src/app/logger.service.ts (class)" region="class">
</code-example>
Here's a `HeroService` that uses a !{_PromiseLinked} to fetch heroes.
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class">
</code-example>
Services are everywhere.
Component classes should be lean. They don't fetch data from the server,
@ -312,6 +518,12 @@ _Dependency injection_ is a way to supply a new instance of a class
with the fully-formed dependencies it requires. Most dependencies are services.
Angular uses dependency injection to provide new components with the services they need.<br class="l-clear-both">Angular can tell which services a component needs by looking at the types of its constructor parameters.
For example, the constructor of your `HeroListComponent` needs a `HeroService`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor">
</code-example>
When Angular creates a component, it first asks an **injector** for
the services that the component requires.
@ -331,7 +543,24 @@ If the injector doesn't have a `HeroService`, how does it know how to make one?
In brief, you must have previously registered a **provider** of the `HeroService` with the injector.
A provider is something that can create or return a service, typically the service class itself.
You can register providers in modules or in components.
In general, add providers to the [root module](guide/architecture#module) so that
the same instance of a service is available everywhere.
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers">
</code-example>
Alternatively, register at a component level in the `providers` property of the `@Component` metadata:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers">
</code-example>
Registering at a component level means you get a new instance of the
service with each new instance of that component.
@ -399,3 +628,6 @@ by implementing the lifecycle hook interfaces.
> [**Router**](guide/router): Navigate from page to page within the client
application and never leave the browser.
> [**Testing**](guide/testing): Run unit tests on your application parts as they interact with the Angular framework
using the _Angular Testing Platform_.

View File

@ -5,6 +5,7 @@ Attribute Directives
Attribute directives attach behavior to elements.
@description
An **Attribute** directive changes the appearance or behavior of a DOM element.
# Contents
@ -48,6 +49,12 @@ The controller class implements the desired directive behavior.
This page demonstrates building a simple _myHighlight_ attribute
directive to set an element's background color
when the user hovers over that element. You can apply it like this:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied">
</code-example>
### Write the directive code
Follow the [setup](guide/setup) instructions for creating a new local project
@ -56,7 +63,9 @@ named <span ngio-ex>attribute-directives</span>.
Create the following source file in the indicated folder:
{@example 'attribute-directives/ts/src/app/highlight.directive.1.ts'}
<code-example path="attribute-directives/src/app/highlight.directive.1.ts">
</code-example>
The `import` statement specifies symbols from the Angular `core`:
@ -74,6 +83,9 @@ is the attribute name in square brackets.
Here, the directive's selector is `[myHighlight]`.
Angular locates all elements in the template that have an attribute named `myHighlight`.
~~~ {.l-sub-section}
### Why not call it "highlight"?
Though *highlight* is a more concise name than *myHighlight* and would work,
@ -84,6 +96,10 @@ This also reduces the risk of colliding with third-party directive names.
Make sure you do **not** prefix the `highlight` directive name with **`ng`** because
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
For a simple demo, the short prefix, `my`, helps distinguish your custom directive.
~~~
After the `@Directive` metadata comes the directive's controller class,
called `HighlightDirective`, which contains the logic for the directive.
<span if-docs="ts">Exporting `HighlightDirective` makes it accessible to other components.</span>
@ -104,19 +120,25 @@ Put the template in its own <span ngio-ex>app.component.html</span>
file that looks like this:
{@example 'attribute-directives/ts/src/app/app.component.1.html'}
<code-example path="attribute-directives/src/app/app.component.1.html">
</code-example>
Now reference this template in the `AppComponent`:
{@example 'attribute-directives/ts/src/app/app.component.ts'}
<code-example path="attribute-directives/src/app/app.component.ts">
</code-example>
Next, add an `import` statement to fetch the `Highlight` directive and
add that class to the `declarations` NgModule metadata. This way Angular
recognizes the directive when it encounters `myHighlight` in the template.
{@example 'attribute-directives/ts/src/app/app.module.ts'}
<code-example path="attribute-directives/src/app/app.module.ts">
</code-example>
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
@ -125,6 +147,9 @@ Now when the app runs, the `myHighlight` directive highlights the paragraph text
</figure>
~~~ {.l-sub-section}
### Your directive isn't working?
Did you remember to add the directive to the `declarations` attribute of `@NgModule`?
@ -141,6 +166,10 @@ Angular detects that you're trying to bind to *something* but it can't find this
in the module's `declarations` array.
After specifying `HighlightDirective` in the `declarations` array,
Angular knows it can apply the directive to components declared in this module.
~~~
To summarize, Angular found the `myHighlight` attribute on the `<p>` element.
It created an instance of the `HighlightDirective` class and
injected a reference to the `<p>` element into the directive's constructor
@ -155,23 +184,50 @@ and respond by setting or clearing the highlight color.
Begin by adding `HostListener` to the list of imported symbols;
add the `Input` symbol as well because you'll need it soon.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="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" title="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.
~~~ {.l-sub-section}
Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually.
There are at least three problems with _that_ approach:
1. You have to write the listeners correctly.
1. The code must *detach* the listener when the directive is destroyed to avoid memory leaks.
1. Talking to DOM API directly isn't a best practice.
~~~
The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`,
which you declare and initialize in the constructor.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor">
</code-example>
Here's the updated directive in full:
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts'}
<code-example path="attribute-directives/src/app/highlight.directive.2.ts">
</code-example>
Run the app and confirm that the background color appears when
the mouse hovers over the `p` and disappears as it moves out.
@ -189,6 +245,12 @@ In this section, you give the developer the power to set the highlight color whi
Start by adding a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color">
</code-example>
{@a input}
### Binding to an _@Input_ property
@ -198,15 +260,45 @@ It's called an *input* property because data flows from the binding expression _
Without that input metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more about that.
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" title="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" title="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" title="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" title="src/app/app.component.html (color)" region="color">
</code-example>
The `[myHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
and sets the directive's highlight color with a property binding.
You're re-using the directive's attribute selector (`[myHighlight]`) to do both jobs.
That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2">
</code-example>
This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent.
@ -216,13 +308,37 @@ This is disagreeable. The word, `myHighlight`, is a terrible property name and i
Fortunately you can name the directive property whatever you want _and_ **_alias it_** for binding purposes.
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" title="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 `myHighlight`.
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" title="src/app/app.component.html (color)" region="color">
</code-example>
Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it.
If someone neglects to bind to `highlightColor`, highlight in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="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" title="src/app/highlight.directive.ts (excerpt)">
</code-example>
## Write a harness to try it
It may be difficult to imagine how this directive actually works.
@ -230,7 +346,19 @@ In this section, you'll turn `AppComponent` into a harness that
lets you pick the highlight color with a radio button and bind your color choice to the directive.
Update <span ngio-ex>app.component.html</span> as follows:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="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" title="src/app/app.component.ts (class)" region="class">
</code-example>
Here are the harness and directive in action.
<figure class='image-display'>
@ -247,13 +375,31 @@ the user picks a highlight color&mdash;is hard-coded as "red".
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" title="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" title="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 `myHighlight` attribute name?
As with components, you can add as many directive property bindings as you need by stringing them along in the template.
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
and fall back to "violet" as the default color.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="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}.
@ -275,39 +421,40 @@ This page covered how to:
The final source code follows:
<md-tab-group>
<md-tab label="app/app.component.ts">
{@example 'attribute-directives/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts">
</code-pane>
<md-tab label="app/app.component.html">
{@example 'attribute-directives/ts/src/app/app.component.html'}
</md-tab>
<code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html">
</code-pane>
<md-tab label="app/highlight.directive.ts">
{@example 'attribute-directives/ts/src/app/highlight.directive.ts'}
</md-tab>
<code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts">
</code-pane>
<md-tab label="app/app.module.ts">
{@example 'attribute-directives/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts">
</code-pane>
<md-tab label="main.ts">
{@example 'attribute-directives/ts/src/main.ts'}
</md-tab>
<code-pane title="main.ts" path="attribute-directives/src/main.ts">
</code-pane>
<md-tab label="index.html">
{@example 'attribute-directives/ts/src/index.html'}
</md-tab>
<code-pane title="index.html" path="attribute-directives/src/index.html">
</code-pane>
</md-tab-group>
</code-tabs>
You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
@ -315,7 +462,19 @@ You can also experience and download the <live-example title="Attribute Directiv
In this demo, the `hightlightColor` 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" title="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" title="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.
Without `@Input`, Angular refuses to bind to the property.
@ -345,6 +504,12 @@ You can tell if `@Input` is needed by the position of the property name in a bin
that property must be adorned with the `@Input` !{_decorator}.
Now apply that reasoning to the following example:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="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.
The `color` property doesn't require the `@Input` !{_decorator}.

View File

@ -5,6 +5,7 @@ Browser support
Browser support and polyfills guide.
@description
Angular supports most recent browsers. This includes the following specific versions:
<table>
@ -237,9 +238,16 @@ Angular supports most recent browsers. This includes the following specific vers
</table>
~~~ {.l-sub-section}
Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request,
using <a href="https://saucelabs.com/" target="_blank">SauceLabs</a> and
<a href="https://www.browserstack.com" target="_blank">Browserstack</a>.
~~~
## Polyfills #
Angular is built on the latest standards of the web platform.
Targeting such a wide range of browsers is challenging because they do not support all features of modern browsers.
@ -247,7 +255,9 @@ Targeting such a wide range of browsers is challenging because they do not suppo
You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
that implement missing features in JavaScript.
{@example 'quickstart/ts/src/index.html' region='polyfills'}
<code-example path="quickstart/src/index.html" region="polyfills" linenums="false">
</code-example>
A particular browser may require at least one polyfill to run _any_ Angular application.
You may need additional polyfills for specific features.

View File

@ -55,21 +55,34 @@ In the following example, we import and register several services
in the `@Component` metadata `providers` array.
{@example 'cb-dependency-injection/ts/src/app/app.component.ts' region='import-services'}
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false">
</code-example>
All of these services are implemented as classes.
Service classes can act as their own providers which is why listing them in the `providers` array
is all the registration we need.
~~~ {.l-sub-section}
A *provider* is something that can create or deliver a service.
Angular creates a service instance from a class provider by "new-ing" it.
Learn more about providers [below](guide/cb-dependency-injection#providers).Now that we've registered these services,
Learn more about providers [below](guide/cb-dependency-injection#providers).
~~~
Now that we've registered these services,
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='ctor'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example>
{@example 'cb-dependency-injection/ts/src/app/user-context.service.ts' region='ctor'}
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false">
</code-example>
<a id="external-module-configuration"></a>
## External module configuration
@ -83,7 +96,9 @@ We see an example of the second case here, where we configure the Component Rout
in the `providers` list of the `AppModule`.
{@example 'cb-dependency-injection/ts/src/app/app.module.ts' region='providers'}
<code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false">
</code-example>
@ -103,13 +118,17 @@ At each step, the consumer of dependencies simply declares what it requires in i
For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`.
{@example 'cb-dependency-injection/ts/src/app/app.component.ts' region='ctor'}
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false">
</code-example>
The `UserContext` in turn has dependencies on both the `LoggerService` (again) and
a `UserService` that gathers information about a particular user.
{@example 'cb-dependency-injection/ts/src/app/user-context.service.ts' region='injectables'}
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false">
</code-example>
When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and
starts to create the `UserContextService`.
@ -128,7 +147,9 @@ Once all the dependencies are in place, the `AppComponent` displays the user inf
### *@Injectable()*
Notice the `@Injectable()`decorator on the `UserContextService` class.
{@example 'cb-dependency-injection/ts/src/app/user-context.service.ts' region='injectable'}
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectable" linenums="false">
</code-example>
That decorator makes it possible for Angular to identify the types of its two dependencies, `LoggerService` and `UserService`.
@ -149,10 +170,17 @@ Some developers prefer to add it only where needed and that's a reasonable polic
~~~
~~~ {.l-sub-section}
The `AppComponent` class had two dependencies as well but no `@Injectable()`.
It didn't need `@Injectable()` because that component class has the `@Component` decorator.
In Angular with TypeScript, a *single* decorator &mdash; *any* decorator &mdash; is sufficient to identify dependency types.
~~~
<a id="service-scope"></a>
## Limit service scope to a component subtree
@ -175,18 +203,27 @@ We can limit the scope of an injected service to a *branch* of the application h
by providing that service *at the sub-root component for that branch*.
Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array:
{@example 'cb-dependency-injection/ts/src/app/sorted-heroes.component.ts' region='injection'}
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="injection">
</code-example>
When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService`
that is visible only to the component and its children (if any).
We could also provide the `HeroService` to a *different* component elsewhere in the application.
That would result in a *different* instance of the service, living in a *different* injector.
~~~ {.l-sub-section}
We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code,
including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`.
Each of these components has its own `HeroService` instance managing its own independent collection of heroes.
~~~
~~~ {.alert.is-helpful}
@ -210,12 +247,16 @@ We call this *sandboxing* because each service and component instance has its ow
<a id="hero-bios-component"></a>
Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`.
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='simple'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple">
</code-example>
Each `HeroBioComponent` can edit a single hero's biography.
A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero.
{@example 'cb-dependency-injection/ts/src/app/hero-cache.service.ts' region='service'}
<code-example path="cb-dependency-injection/src/app/hero-cache.service.ts" region="service">
</code-example>
Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`.
They'd be competing with each other to determine which hero to cache.
@ -223,7 +264,9 @@ They'd be competing with each other to determine which hero to cache.
Each `HeroBioComponent` gets its *own* `HeroCacheService` instance
by listing the `HeroCacheService` in its metadata `providers` array.
{@example 'cb-dependency-injection/ts/src/app/hero-bio.component.ts' region='component'}
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component">
</code-example>
The parent `HeroBiosComponent` binds a value to the `heroId`.
The `ngOnInit` pass that `id` to the service which fetches and caches the hero.
@ -266,17 +309,23 @@ We look at this second, more interesting case in our next example.
### Demonstration
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component).
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='hero-bios-and-contacts'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts">
</code-example>
Focus on the template:
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='template'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false">
</code-example>
We've inserted a `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
{@example 'cb-dependency-injection/ts/src/app/hero-bio.component.ts' region='template'}
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="template" linenums="false">
</code-example>
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
<figure class='image-display'>
@ -285,11 +334,15 @@ It looks like this, with the hero's telephone number from `HeroContactComponent`
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
{@example 'cb-dependency-injection/ts/src/app/hero-contact.component.ts' region='component'}
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component">
</code-example>
Focus on the constructor parameters
{@example 'cb-dependency-injection/ts/src/app/hero-contact.component.ts' region='ctor-params'}
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false">
</code-example>
The `@Host()` function decorating the `heroCache` property ensures that
we get a reference to the cache service from the parent `HeroBioComponent`.
@ -302,7 +355,14 @@ The host `HeroBioComponent` doesn't have its own `LoggerService` provider.
Angular would throw an error if we hadn't also decorated the property with the `@Optional()` function.
Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest of the component adapts.
We'll come back to the `elementRef` property shortly.Here's the `HeroBiosAndContactsComponent` in action.
~~~ {.l-sub-section}
We'll come back to the `elementRef` property shortly.
~~~
Here's the `HeroBiosAndContactsComponent` in action.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img>
</figure>
@ -327,7 +387,9 @@ require DOM access.
To illustrate, we've written a simplified version of the `HighlightDirective` from
the [Attribute Directives](guide/attribute-directives) chapter.
{@example 'cb-dependency-injection/ts/src/app/highlight.directive.ts'}
<code-example path="cb-dependency-injection/src/app/highlight.directive.ts">
</code-example>
The directive sets the background to a highlight color when the user mouses over the
DOM element to which it is applied.
@ -339,7 +401,9 @@ Its `nativeElement` property exposes the DOM element for the directive to manipu
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.
{@example 'cb-dependency-injection/ts/src/app/app.component.html' region='highlight'}
<code-example path="cb-dependency-injection/src/app/app.component.html" region="highlight" linenums="false">
</code-example>
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
<figure class='image-display'>
@ -360,7 +424,9 @@ Angular passes this token to the injector and assigns the result to the paramete
Here's a typical example:
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='ctor'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example>
Angular asks the injector for the service associated with the `LoggerService`
and assigns the returned value to the `logger` parameter.
@ -369,23 +435,34 @@ Where did the injector get that value?
It may already have that value in its internal container.
If it doesn't, it may be able to make one with the help of a ***provider***.
A *provider* is a recipe for delivering a service associated with a *token*.
~~~ {.l-sub-section}
If the injector doesn't have a provider for the requested *token*, it delegates the request
to its parent injector, where the process repeats until there are no more injectors.
If the search is futile, the injector throws an error ... unless the request was [optional](guide/cb-dependency-injection#optional).
Let's return our attention to providers themselves.A new injector has no providers.
Let's return our attention to providers themselves.
~~~
A new injector has no providers.
Angular initializes the injectors it creates with some providers it cares about.
We have to register our _own_ application providers manually,
usually in the `providers` array of the `Component` or `Directive` metadata:
{@example 'cb-dependency-injection/ts/src/app/app.component.ts' region='providers'}
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="providers">
</code-example>
### Defining providers
The simple class provider is the most typical by far.
We mention the class in the `providers` array and we're done.
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='class-provider'}
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="class-provider" linenums="false">
</code-example>
It's that simple because the most common injected service is an instance of a class.
But not every dependency can be satisfied by creating a new instance of a class.
@ -399,7 +476,9 @@ The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='hero-of-the-month'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month">
</code-example>
@ -427,7 +506,9 @@ The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class;
the second specifies a literal string resource:
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='use-value'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-value" linenums="false">
</code-example>
The `Hero` provider token is a class which makes sense because the value is a `Hero`
and the consumer of the injected hero would want the type information.
@ -440,7 +521,9 @@ The value of a *value provider* must be defined *now*. We can't create the value
Obviously the title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file:
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='some-hero'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="some-hero">
</code-example>
The other providers create their values *lazily* when they're needed for injection.
@ -457,7 +540,9 @@ or fake the behavior of the real class in a test case.
We see two examples in the `HeroOfTheMonthComponent`:
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='use-class'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-class" linenums="false">
</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 injection token.
@ -466,10 +551,19 @@ We wrote it in this long form to de-mystify the preferred short form.
The second provider substitutes the `DateLoggerService` for the `LoggerService`.
The `LoggerService` is already registered at the `AppComponent` level.
When _this component_ requests the `LoggerService`, it receives the `DateLoggerService` instead.
This component and its tree of child components receive the `DateLoggerService` instance.
Components outside the tree continue to receive the original `LoggerService` instance.The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='date-logger-service'}
~~~ {.l-sub-section}
This component and its tree of child components receive the `DateLoggerService` instance.
Components outside the tree continue to receive the original `LoggerService` instance.
~~~
The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false">
</code-example>
@ -481,7 +575,9 @@ The `useExisting` provider maps one token to another.
In effect, the first token is an ***alias*** for the service associated with second token,
creating ***two ways to access the same service object***.
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='use-existing'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example>
Narrowing an API through an aliasing interface is _one_ important use case for this technique.
We're aliasing for that very purpose here.
@ -489,7 +585,9 @@ Imagine that the `LoggerService` had a large API (it's actually only three metho
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface):
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='minimal-logger'}
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example>
The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript:
<figure class='image-display'>
@ -512,7 +610,9 @@ The following image, which displays the logging date, confirms the point:
The `useFactory` provider creates a dependency object by calling a factory function
as seen in this example.
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='use-factory'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory">
</code-example>
Use this technique to ***create a dependency object***
with a factory function whose inputs are some ***combination of injected services and local state***.
@ -528,7 +628,9 @@ The `runnersUpFactory` itself isn't the provider factory function.
The true provider factory function is the function that `runnersUpFactory` returns.
{@example 'cb-dependency-injection/ts/src/app/runners-up.ts' region='factory-synopsis'}
<code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false">
</code-example>
That returned function takes a winning `Hero` and a `HeroService` as arguments.
@ -540,12 +642,19 @@ to provide these factory function dependencies.
After some undisclosed work, the function returns the string of names
and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`.
~~~ {.l-sub-section}
The function retrieves candidate heroes from the `HeroService`,
takes `2` of them to be the runners-up, and returns their concatenated names.
Look at the <live-example name="cb-dependency-injection"></live-example>
for the full source code.
~~~
{@a tokens}
## Provider token alternatives: the *class-interface* and *OpaqueToken*
@ -562,11 +671,15 @@ That's the subject of our next section.
In the previous *Hero of the Month* example, we used the `MinimalLogger` class
as the token for a provider of a `LoggerService`.
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='use-existing'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example>
The `MinimalLogger` is an abstract class.
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='minimal-logger'}
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example>
We usually inherit from an abstract class.
But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it.
@ -574,7 +687,9 @@ Instead, we use it like an interface.
Look again at the declaration for `DateLoggerService`
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='date-logger-service-signature'}
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service-signature" linenums="false">
</code-example>
`DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`.
The `DateLoggerService` *implements* `MinimalLogger` as if `MinimalLogger` were an *interface*.
@ -587,6 +702,9 @@ A ***class-interface*** should define *only* the members that its consumers are
Such a narrowing interface helps decouple the concrete class from its consumers.
The `MinimalLogger` defines just two of the `LoggerClass` members.
~~~ {.l-sub-section}
#### Why *MinimalLogger* is a class and not an interface
We can't use an interface as a provider token because
interfaces are not JavaScript objects.
@ -601,11 +719,17 @@ Using a class as an interface gives us the characteristics of an interface in a
The minimize memory cost, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript:
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='minimal-logger-transpiled'}
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger-transpiled" linenums="false">
</code-example>
It never grows larger no matter how many members we add *as long as they are typed but not implemented*.
~~~
{@a opaque-token}
### OpaqueToken
@ -621,11 +745,15 @@ The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='provide-opaque-token'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false">
</code-example>
We created the `TITLE` token like this:
{@example 'cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts' region='opaque-token'}
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false">
</code-example>
@ -649,14 +777,23 @@ It demands its own instance of the `HeroService` to get heroes
and displays them in the order they arrive from the database.
{@example 'cb-dependency-injection/ts/src/app/sorted-heroes.component.ts' region='heroes-base'}
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base">
</code-example>
~~~ {.l-sub-section}
We strongly prefer simple constructors. They should do little more than initialize variables.
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
That's why we call the `HeroService` from within the `ngOnInit` rather than the constructor.
We explain the mysterious `afterGetHeroes` below.Users want to see the heroes in alphabetical order.
We explain the mysterious `afterGetHeroes` below.
~~~
Users want to see the heroes in alphabetical order.
Rather than modify the original component, we sub-class it and create a
`SortedHeroesComponent` that sorts the heroes before presenting them.
The `SortedHeroesComponent` lets the base class fetch the heroes.
@ -667,7 +804,9 @@ We must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor.
{@example 'cb-dependency-injection/ts/src/app/sorted-heroes.component.ts' region='sorted-heroes'}
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes">
</code-example>
Now take note of the `afterGetHeroes` method.
Our first instinct was to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there.
@ -711,12 +850,16 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex}
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-1'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false">
</code-example>
*Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='cathy'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="cathy" linenums="false">
</code-example>
We added the [@Optional](guide/cb-dependency-injection#optional) qualifier for safety but
the <live-example name="cb-dependency-injection"></live-example>
@ -736,19 +879,30 @@ The app probably defines more than a dozen financial instrument components.
If we're lucky, they all implement the same base class
whose API our `NewsComponent` understands.
~~~ {.l-sub-section}
Looking for components that implement an interface would be better.
That's not possible because TypeScript interfaces disappear from the transpiled JavaScript
which doesn't support interfaces. There's no artifact we could look for.We're not claiming this is good design.
which doesn't support interfaces. There's no artifact we could look for.
~~~
We're not claiming this is good design.
We are asking *can a component inject its parent via the parent's base class*?
The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex)
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-class-signature'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='craig'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="craig" linenums="false">
</code-example>
Unfortunately, this does not work.
The <live-example name="cb-dependency-injection"></live-example>
@ -772,14 +926,18 @@ and add that provider to the `providers` array of the `@Component` metadata for
{@a alex-providers}
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-providers'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example>
[Parent](guide/cb-dependency-injection#parent-token) is the provider's *class-interface* token.
The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself.
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='carol-class'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false">
</code-example>
Here's *Alex* and family in action:
<figure class='image-display'>
@ -800,25 +958,28 @@ That means he must both *inject* the `Parent` *class-interface* to get *Alice* a
Here's *Barry*:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='barry'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false">
</code-example>
*Barry*'s `providers` array looks just like [*Alex*'s](guide/cb-dependency-injection#alex-providers).
If we're going to keep writing [*alias providers*](guide/cb-dependency-injection#useexisting) like this we should create a [helper function](guide/cb-dependency-injection#provideparent).
For now, focus on *Barry*'s constructor:
<md-tab-group>
<md-tab label="Barry's constructor">
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='barry-ctor'}
</md-tab>
<code-tabs>
<code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor">
</code-pane>
<md-tab label="Carol's constructor">
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='carol-ctor'}
</md-tab>
<code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor">
</code-pane>
</md-tab-group>
</code-tabs>
It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator.
@ -845,7 +1006,9 @@ We [learned earlier](guide/cb-dependency-injection#class-interface) that a *clas
Our example defines a `Parent` *class-interface* .
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='parent'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="parent" linenums="false">
</code-example>
The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*.,
The `name` property is the only member of a parent component that a child component can call.
@ -853,45 +1016,66 @@ Such a narrowing interface helps decouple the child component class from its par
A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alice-class-signature'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false">
</code-example>
Doing so adds clarity to the code. But it's not technically necessary.
Although the `AlexComponent` has a `name` property (as required by its `Base` class)
its class signature doesn't mention `Parent`:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-class-signature'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example>
~~~ {.l-sub-section}
The `AlexComponent` *should* implement `Parent` as a matter of proper style.
It doesn't in this example *only* to demonstrate that the code will compile and run without the interface
~~~
{@a provideparent}
### A *provideParent* helper function
Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref):
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-providers'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example>
We can extract that logic into a helper function like this:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='provide-the-parent'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-the-parent" linenums="false">
</code-example>
Now we can add a simpler, more meaningful parent provider to our components:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alice-providers'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-providers" linenums="false">
</code-example>
We can do better. The current version of the helper function can only alias the `Parent` *class-interface*.
Our application might have a variety of parent types, each with its own *class-interface* token.
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='provide-parent'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false">
</code-example>
And here's how we could use it with a different parent type:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='beth-providers'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false">
</code-example>
@ -917,5 +1101,7 @@ appear *above* the class definition.
We break the circularity with `forwardRef`:
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-providers'}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example>

View File

@ -7,22 +7,31 @@ A collection of recipes for common Angular application scenarios.
@description
The *Cookbook* offers answers to common implementation questions.
Each cookbook chapter is a collection of recipes focused on a particular Angular feature or application challenge
Each cookbook page is a collection of recipes focused on a particular Angular feature or application challenge
such as data binding, cross-component interaction, and communicating with a remote server via HTTP.
The cookbook is just getting started. Many more recipes are on the way.
Each cookbook chapter links to a live sample with every recipe included.
Recipes are deliberately brief and code-centric.
Each recipe links to a chapter of the Developer Guide or the API Guide
where you can learn more about the purpose, context, and design choices behind the code snippets.
~~~ {.l-sub-section}
The cookbook is just getting started. Many more recipes are on the way.
~~~
Each cookbook links to a live sample with every recipe included.
Recipes are deliberately brief and code-centric.
Each recipe links to a relevant page of the [Developer Guide](guide) or the
[API Reference](api) where you can learn more about
the purpose, context, and design choices behind the code snippets.
## Feedback
The cookbook is a perpetual *work-in-progress*.
We welcome feedback! Leave a comment by clicking the icon in upper right corner of the banner.
Post *documentation* issues and pull requests on the
[angular.io](https://github.com/angular/angular.io) github repository.
Post *documentation* requests and comments as
<a href="https://github.com/angular/angular.io/issues" target="_blank" title="Documentation issues on github">
<i>issues</i> on the angular.io</a> github repository.
Fixes (small ones) are greatly appreciated as
<a href="https://github.com/angular/angular.io/pulls" target="_blank" title="Documentation PRs on github">
<i>pull requests</i></a>.
Post issues with *Angular itself* to the [angular](https://github.com/angular/angular) github repository.

View File

@ -5,9 +5,19 @@ Change Log
An annotated history of recent documentation improvements.
@description
The Angular documentation is a living document with continuous improvements.
This log calls attention to recent significant changes.
## Updated to Angular 4.0. Documentation for Angular 2.x can be found at [v2.angular.io](https://v2.angular.io).
## All mention of moduleId removed. "Component relative paths" cookbook deleted (2017-03-13)
We added a new SystemJS plugin (systemjs-angular-loader.js) to our recommended SystemJS configuration.
This plugin dynamically converts "component-relative" paths in templateUrl and styleUrls to "absolute paths" for you.
We strongly encourage you to only write component-relative paths.
That is the only form of URL discussed in these docs. You no longer need to write @Component({ moduleId: module.id }), nor should you.
## NEW: Downloadable examples for each guide (2017-02-28)
Now you can download the sample code for any guide and run it locally.
Look for the new download links next to the "live example" links.

View File

@ -1,3 +1,395 @@
@title
Cheat Sheet
@description
// SCSS from
// resources/css/module/_cheatsheet.scss
// resources/css/layout/_grids.scss: grid-fluid
// resources/css/layout/_layout.scss: docs-content, l-content-small
<div class="l-content-small grid-fluid docs-content cheatsheet">
<table>
<tbody><tr>
<th>Bootstrapping</th>
<th><p><code>import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';</code>
</p>
</th>
</tr>
<tr>
<td><code><b>platformBrowserDynamic().bootstrapModule</b>(AppModule);</code></td>
<td><p>Bootstraps the app, using the root component from the specified <code>NgModule</code>. </p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>NgModules</th>
<th><p><code>import { NgModule } from '@angular/core';</code>
</p>
</th>
</tr>
<tr>
<td><code>@<b>NgModule</b>({&nbsp;declarations:&nbsp;...,&nbsp;imports:&nbsp;...,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exports:&nbsp;...,&nbsp;providers:&nbsp;...,&nbsp;bootstrap:&nbsp;...})<br>class&nbsp;MyModule&nbsp;{}</code></td>
<td><p>Defines a module that contains components, directives, pipes, and providers.</p>
</td>
</tr><tr>
<td><code><b>declarations:</b>&nbsp;[MyRedComponent,&nbsp;MyBlueComponent,&nbsp;MyDatePipe]</code></td>
<td><p>List of components, directives, and pipes that belong to this module.</p>
</td>
</tr><tr>
<td><code><b>imports:</b>&nbsp;[BrowserModule,&nbsp;SomeOtherModule]</code></td>
<td><p>List of modules to import into this module. Everything from the imported modules
is available to <code>declarations</code> of this module.</p>
</td>
</tr><tr>
<td><code><b>exports:</b>&nbsp;[MyRedComponent,&nbsp;MyDatePipe]</code></td>
<td><p>List of components, directives, and pipes visible to modules that import this module.</p>
</td>
</tr><tr>
<td><code><b>providers:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><p>List of dependency injection providers visible both to the contents of this module and to importers of this module.</p>
</td>
</tr><tr>
<td><code><b>bootstrap:</b>&nbsp;[MyAppComponent]</code></td>
<td><p>List of components to bootstrap when this module is bootstrapped.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Template syntax</th>
<th></th>
</tr>
<tr>
<td><code>&lt;input&nbsp;<b>[value]</b>="firstName"&gt;</code></td>
<td><p>Binds property <code>value</code> to the result of expression <code>firstName</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[attr.role]</b>="myAriaRole"&gt;</code></td>
<td><p>Binds attribute <code>role</code> to the result of expression <code>myAriaRole</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[class.extra-sparkle]</b>="isDelightful"&gt;</code></td>
<td><p>Binds the presence of the CSS class <code>extra-sparkle</code> on the element to the truthiness of the expression <code>isDelightful</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[style.width.px]</b>="mySize"&gt;</code></td>
<td><p>Binds style property <code>width</code> to the result of expression <code>mySize</code> in pixels. Units are optional.</p>
</td>
</tr><tr>
<td><code>&lt;button&nbsp;<b>(click)</b>="readRainbow($event)"&gt;</code></td>
<td><p>Calls method <code>readRainbow</code> when a click event is triggered on this button element (or its children) and passes in the event object.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;title="Hello&nbsp;<b>{{ponyName}}</b>"&gt;</code></td>
<td><p>Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
<code>&lt;div [title]="'Hello ' + ponyName"&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Hello&nbsp;<b>{{ponyName}}</b>&lt;/p&gt;</code></td>
<td><p>Binds text content to an interpolated string, for example, "Hello Seabiscuit".</p>
</td>
</tr><tr>
<td><code>&lt;my-cmp&nbsp;<b>[(title)]</b>="name"&gt;</code></td>
<td><p>Sets up two-way data binding. Equivalent to: <code>&lt;my-cmp [title]="name" (titleChange)="name=$event"&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;video&nbsp;<b>#movieplayer</b>&nbsp;...&gt;<br>&nbsp;&nbsp;&lt;button&nbsp;<b>(click)</b>="movieplayer.play()"&gt;<br>&lt;/video&gt;</code></td>
<td><p>Creates a local variable <code>movieplayer</code> that provides access to the <code>video</code> element instance in data-binding and event-binding expressions in the current template.</p>
</td>
</tr><tr>
<td><code>&lt;p&nbsp;<b>*myUnless</b>="myExpression"&gt;...&lt;/p&gt;</code></td>
<td><p>The <code>*</code> symbol turns the current element into an embedded template. Equivalent to:
<code>&lt;template [myUnless]="myExpression"&gt;&lt;p&gt;...&lt;/p&gt;&lt;/template&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Card&nbsp;No.:&nbsp;<b>{{cardNumber&nbsp;|&nbsp;myCardNumberFormatter}}</b>&lt;/p&gt;</code></td>
<td><p>Transforms the current value of expression <code>cardNumber</code> via the pipe called <code>myCardNumberFormatter</code>.</p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Employer:&nbsp;<b>{{employer?.companyName}}</b>&lt;/p&gt;</code></td>
<td><p>The safe navigation operator (<code>?</code>) means that the <code>employer</code> field is optional and if <code>undefined</code>, the rest of the expression should be ignored.</p>
</td>
</tr><tr>
<td><code>&lt;<b>svg:</b>rect&nbsp;x="0"&nbsp;y="0"&nbsp;width="100"&nbsp;height="100"/&gt;</code></td>
<td><p>An SVG snippet template needs an <code>svg:</code> prefix on its root element to disambiguate the SVG element from an HTML component.</p>
</td>
</tr><tr>
<td><code>&lt;<b>svg</b>&gt;<br>&nbsp;&nbsp;&lt;rect&nbsp;x="0"&nbsp;y="0"&nbsp;width="100"&nbsp;height="100"/&gt;<br>&lt;/<b>svg</b>&gt;</code></td>
<td><p>An <code>&lt;svg&gt;</code> root element is detected as an SVG element automatically, without the prefix.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Built-in directives</th>
<th><p><code>import { CommonModule } from '@angular/common';</code>
</p>
</th>
</tr>
<tr>
<td><code>&lt;section&nbsp;<b>*ngIf</b>="showSection"&gt;</code></td>
<td><p>Removes or recreates a portion of the DOM tree based on the <code>showSection</code> expression.</p>
</td>
</tr><tr>
<td><code>&lt;li&nbsp;<b>*ngFor</b>="let&nbsp;item&nbsp;of&nbsp;list"&gt;</code></td>
<td><p>Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[ngSwitch]</b>="conditionExpression"&gt;<br>&nbsp;&nbsp;&lt;template&nbsp;<b>[<b>ngSwitchCase</b>]</b>="case1Exp"&gt;...&lt;/template&gt;<br>&nbsp;&nbsp;&lt;template&nbsp;<b>ngSwitchCase</b>="case2LiteralString"&gt;...&lt;/template&gt;<br>&nbsp;&nbsp;&lt;template&nbsp;<b>ngSwitchDefault</b>&gt;...&lt;/template&gt;<br>&lt;/div&gt;</code></td>
<td><p>Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of <code>conditionExpression</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[ngClass]</b>="{active:&nbsp;isActive,&nbsp;disabled:&nbsp;isDisabled}"&gt;</code></td>
<td><p>Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Forms</th>
<th><p><code>import { FormsModule } from '@angular/forms';</code>
</p>
</th>
</tr>
<tr>
<td><code>&lt;input&nbsp;<b>[(ngModel)]</b>="userName"&gt;</code></td>
<td><p>Provides two-way data-binding, parsing, and validation for form controls.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Class decorators</th>
<th><p><code>import { Directive, ... } from '@angular/core';</code>
</p>
</th>
</tr>
<tr>
<td><code><b>@Component({...})</b><br>class&nbsp;MyComponent()&nbsp;{}</code></td>
<td><p>Declares that a class is a component and provides metadata about the component.</p>
</td>
</tr><tr>
<td><code><b>@Directive({...})</b><br>class&nbsp;MyDirective()&nbsp;{}</code></td>
<td><p>Declares that a class is a directive and provides metadata about the directive.</p>
</td>
</tr><tr>
<td><code><b>@Pipe({...})</b><br>class&nbsp;MyPipe()&nbsp;{}</code></td>
<td><p>Declares that a class is a pipe and provides metadata about the pipe.</p>
</td>
</tr><tr>
<td><code><b>@Injectable()</b><br>class&nbsp;MyService()&nbsp;{}</code></td>
<td><p>Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Directive configuration</th>
<th><p><code>@Directive({ property1: value1, ... })</code>
</p>
</th>
</tr>
<tr>
<td><code><b>selector:</b>&nbsp;'.cool-button:not(a)'</code></td>
<td><p>Specifies a CSS selector that identifies this directive within a template. Supported selectors include <code>element</code>,
<code>[attribute]</code>, <code>.class</code>, and <code>:not()</code>.</p>
<p>Does not support parent-child relationship selectors.</p>
</td>
</tr><tr>
<td><code><b>providers:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><p>List of dependency injection providers for this directive and its children.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Component configuration</th>
<th><p>
<code>@Component</code> extends <code>@Directive</code>,
so the <code>@Directive</code> configuration applies to components as well</p>
</th>
</tr>
<tr>
<td><code><b>moduleId:</b>&nbsp;module.id</code></td>
<td><p>If set, the <code>templateUrl</code> and <code>styleUrl</code> are resolved relative to the component.</p>
</td>
</tr><tr>
<td><code><b>viewProviders:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><p>List of dependency injection providers scoped to this component's view.</p>
</td>
</tr><tr>
<td><code><b>template:</b>&nbsp;'Hello&nbsp;{{name}}'<br><b>templateUrl:</b>&nbsp;'my-component.html'</code></td>
<td><p>Inline template or external template URL of the component's view.</p>
</td>
</tr><tr>
<td><code><b>styles:</b>&nbsp;['.primary&nbsp;{color:&nbsp;red}']<br><b>styleUrls:</b>&nbsp;['my-component.css']</code></td>
<td><p>List of inline CSS styles or external stylesheet URLs for styling the components view.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Class field decorators for directives and components</th>
<th><p><code>import { Input, ... } from '@angular/core';</code>
</p>
</th>
</tr>
<tr>
<td><code><b>@Input()</b>&nbsp;myProperty;</code></td>
<td><p>Declares an input property that you can update via property binding (example:
<code>&lt;my-cmp [myProperty]="someExpression"&gt;</code>).</p>
</td>
</tr><tr>
<td><code><b>@Output()</b>&nbsp;myEvent&nbsp;=&nbsp;new&nbsp;EventEmitter();</code></td>
<td><p>Declares an output property that fires events that you can subscribe to with an event binding (example: <code>&lt;my-cmp (myEvent)="doSomething()"&gt;</code>).</p>
</td>
</tr><tr>
<td><code><b>@HostBinding('class.valid')</b>&nbsp;isValid;</code></td>
<td><p>Binds a host element property (here, the CSS class <code>valid</code>) to a directive/component property (<code>isValid</code>).</p>
</td>
</tr><tr>
<td><code><b>@HostListener('click',&nbsp;['$event'])</b>&nbsp;onClick(e)&nbsp;{...}</code></td>
<td><p>Subscribes to a host element event (<code>click</code>) with a directive/component method (<code>onClick</code>), optionally passing an argument (<code>$event</code>).</p>
</td>
</tr><tr>
<td><code><b>@ContentChild(myPredicate)</b>&nbsp;myChildComponent;</code></td>
<td><p>Binds the first result of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class.</p>
</td>
</tr><tr>
<td><code><b>@ContentChildren(myPredicate)</b>&nbsp;myChildComponents;</code></td>
<td><p>Binds the results of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class.</p>
</td>
</tr><tr>
<td><code><b>@ViewChild(myPredicate)</b>&nbsp;myChildComponent;</code></td>
<td><p>Binds the first result of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class. Not available for directives.</p>
</td>
</tr><tr>
<td><code><b>@ViewChildren(myPredicate)</b>&nbsp;myChildComponents;</code></td>
<td><p>Binds the results of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class. Not available for directives.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Directive and component change detection and lifecycle hooks</th>
<th><p>(implemented as class methods)
</p>
</th>
</tr>
<tr>
<td><code><b>constructor(myService:&nbsp;MyService,&nbsp;...)</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.</p>
</td>
</tr><tr>
<td><code><b>ngOnChanges(changeRecord)</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after every change to input properties and before processing content or child views.</p>
</td>
</tr><tr>
<td><code><b>ngOnInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after the constructor, initializing input properties, and the first call to <code>ngOnChanges</code>.</p>
</td>
</tr><tr>
<td><code><b>ngDoCheck()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check.</p>
</td>
</tr><tr>
<td><code><b>ngAfterContentInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after <code>ngOnInit</code> when the component's or directive's content has been initialized.</p>
</td>
</tr><tr>
<td><code><b>ngAfterContentChecked()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after every check of the component's or directive's content.</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after <code>ngAfterContentInit</code> when the component's view has been initialized. Applies to components only.</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewChecked()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called after every check of the component's view. Applies to components only.</p>
</td>
</tr><tr>
<td><code><b>ngOnDestroy()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><p>Called once, before the instance is destroyed.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Dependency injection configuration</th>
<th></th>
</tr>
<tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyService,&nbsp;<b>useClass</b>:&nbsp;MyMockService&nbsp;}</code></td>
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>MyMockService</code> class.</p>
</td>
</tr><tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyService,&nbsp;<b>useFactory</b>:&nbsp;myFactory&nbsp;}</code></td>
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>myFactory</code> factory function.</p>
</td>
</tr><tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyValue,&nbsp;<b>useValue</b>:&nbsp;41&nbsp;}</code></td>
<td><p>Sets or overrides the provider for <code>MyValue</code> to the value <code>41</code>.</p>
</td>
</tr>
</tbody></table>
<table>
<tbody><tr>
<th>Routing and navigation</th>
<th><p><code>import { Routes, RouterModule, ... } from '@angular/router';</code>
</p>
</th>
</tr>
<tr>
<td><code>const&nbsp;routes:&nbsp;<b>Routes</b>&nbsp;=&nbsp;[<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'',&nbsp;component:&nbsp;HomeComponent&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'path/:routeParam',&nbsp;component:&nbsp;MyComponent&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'staticPath',&nbsp;component:&nbsp;...&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'**',&nbsp;component:&nbsp;...&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'oldPath',&nbsp;redirectTo:&nbsp;'/staticPath'&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;...,&nbsp;component:&nbsp;...,&nbsp;data:&nbsp;{&nbsp;message:&nbsp;'Custom'&nbsp;}&nbsp;}<br>]);<br><br>const&nbsp;routing&nbsp;=&nbsp;RouterModule.forRoot(routes);</code></td>
<td><p>Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.</p>
</td>
</tr><tr>
<td><code><br>&lt;<b>router-outlet</b>&gt;&lt;/<b>router-outlet</b>&gt;<br>&lt;<b>router-outlet</b>&nbsp;name="aux"&gt;&lt;/<b>router-outlet</b>&gt;<br></code></td>
<td><p>Marks the location to load the component of the active route.</p>
</td>
</tr><tr>
<td><code><br>&lt;a&nbsp;routerLink="/path"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path',&nbsp;routeParam&nbsp;]"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path',&nbsp;{&nbsp;matrixParam:&nbsp;'value'&nbsp;}&nbsp;]"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path'&nbsp;]"&nbsp;[queryParams]="{&nbsp;page:&nbsp;1&nbsp;}"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path'&nbsp;]"&nbsp;fragment="anchor"&gt;<br></code></td>
<td><p>Creates a link to a different view based on a route instruction consisting of a route path, required and optional parameters, query parameters, and a fragment. To navigate to a root route, use the <code>/</code> prefix; for a child route, use the <code>./</code>prefix; for a sibling or parent, use the <code>../</code> prefix.</p>
</td>
</tr><tr>
<td><code>&lt;a&nbsp;[routerLink]="[&nbsp;'/path'&nbsp;]"&nbsp;routerLinkActive="active"&gt;</code></td>
<td><p>The provided classes are added to the element when the <code>routerLink</code> becomes the current active route.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanActivate</b>Guard&nbsp;implements&nbsp;<b>CanActivate</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canActivate(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canActivate:&nbsp;[<b>CanActivate</b>Guard]&nbsp;}</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should activate this component. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanDeactivate</b>Guard&nbsp;implements&nbsp;<b>CanDeactivate</b>&lt;T&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canDeactivate(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;component:&nbsp;T,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canDeactivate:&nbsp;[<b>CanDeactivate</b>Guard]&nbsp;}</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should deactivate this component after a navigation. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanActivateChild</b>Guard&nbsp;implements&nbsp;<b>CanActivateChild</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canActivateChild(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canActivateChild:&nbsp;[CanActivateGuard],<br>&nbsp;&nbsp;&nbsp;&nbsp;children:&nbsp;...&nbsp;}</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should activate the child route. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>Resolve</b>Guard&nbsp;implements&nbsp;<b>Resolve</b>&lt;T&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;resolve(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;any&gt;|Promise&lt;any&gt;|any&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;resolve:&nbsp;[<b>Resolve</b>Guard]&nbsp;}</code></td>
<td><p>An interface for defining a class that the router should call first to resolve route data before rendering the route. Should return a value or an Observable/Promise that resolves to a value.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanLoad</b>Guard&nbsp;implements&nbsp;<b>CanLoad</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canLoad(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;Route<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canLoad:&nbsp;[<b>CanLoad</b>Guard],&nbsp;loadChildren:&nbsp;...&nbsp;}</code></td>
<td><p>An interface for defining a class that the router should call first to check if the lazy loaded module should be loaded. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr>
</tbody></table>
</div>

View File

@ -7,39 +7,36 @@ Share information between different directives and components.
@description
<a id="top"></a>This cookbook contains recipes for common component communication scenarios
in which two or more components share information.
<a id="toc"></a>## Table of contents
<a id="toc"></a># Contents
[Pass data from parent to child with input binding](guide/component-communication#parent-to-child)
- [Pass data from parent to child with input binding](guide/component-communication#parent-to-child)
- [Intercept input property changes with a setter](guide/component-communication#parent-to-child-setter)
- [Intercept input property changes with `ngOnChanges()`](guide/component-communication#parent-to-child-on-changes)
- [Parent calls an `@ViewChild()`](guide/component-communication#parent-to-view-child)
- [Parent and children communicate via a service](guide/component-communication#bidirectional-service)
[Intercept input property changes with a setter](guide/component-communication#parent-to-child-setter)
[Intercept input property changes with *ngOnChanges*](guide/component-communication#parent-to-child-on-changes)
[Parent listens for child event](guide/component-communication#child-to-parent)
[Parent interacts with child via a *local variable*](guide/component-communication#parent-to-child-local-var)
[Parent calls a *ViewChild*](guide/component-communication#parent-to-view-child)
[Parent and children communicate via a service](guide/component-communication#bidirectional-service)
**See the <live-example name="cb-component-communication"></live-example>**.
<a id="parent-to-child"></a>## Pass data from parent to child with input binding
`HeroChildComponent` has two ***input properties***,
`HeroChildComponent` has two ***input properties***,
typically adorned with [@Input decorations](guide/template-syntax).
{@example 'cb-component-communication/ts/src/app/hero-child.component.ts'}
<code-example path="cb-component-communication/src/app/hero-child.component.ts">
</code-example>
The second `@Input` aliases the child component property name `masterName` as `'master'`.
The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater,
binding its `master` string property to the child's `master` alias
The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater,
binding its `master` string property to the child's `master` alias,
and each iteration's `hero` instance to the child's `hero` property.
{@example 'cb-component-communication/ts/src/app/hero-parent.component.ts'}
<code-example path="cb-component-communication/src/app/hero-parent.component.ts">
</code-example>
The running application displays three heroes:
@ -52,7 +49,9 @@ The running application displays three heroes:
E2E test that all children were instantiated and displayed as expected:
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child'}
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child">
</code-example>
[Back to top](guide/component-communication#top)
@ -60,16 +59,20 @@ E2E test that all children were instantiated and displayed as expected:
Use an input property setter to intercept and act upon a value from the parent.
The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text.
The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text.
{@example 'cb-component-communication/ts/src/app/name-child.component.ts'}
<code-example path="cb-component-communication/src/app/name-child.component.ts">
</code-example>
Here's the `NameParentComponent` demonstrating name variations including a name with all spaces:
{@example 'cb-component-communication/ts/src/app/name-parent.component.ts'}
<code-example path="cb-component-communication/src/app/name-parent.component.ts">
</code-example>
<figure class='image-display'>
@ -81,24 +84,37 @@ Here's the `NameParentComponent` demonstrating name variations including a name
E2E tests of input property setter with empty and non-empty names:
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child-setter'}
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter">
</code-example>
[Back to top](guide/component-communication#top)
<a id="parent-to-child-on-changes"></a>## Intercept input property changes with *ngOnChanges*
<a id="parent-to-child-on-changes"></a>## Intercept input property changes with *ngOnChanges()*
Detect and act upon changes to input property values with the `ngOnChanges` method of the `OnChanges` lifecycle hook interface.
May prefer this approach to the property setter when watching multiple, interacting input properties.
Detect and act upon changes to input property values with the `ngOnChanges()` method of the `OnChanges` lifecycle hook interface.
Learn about `ngOnChanges` in the [LifeCycle Hooks](guide/lifecycle-hooks) chapter.This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
~~~ {.l-sub-section}
You may prefer this approach to the property setter when watching multiple, interacting input properties.
Learn about `ngOnChanges()` in the [LifeCycle Hooks](guide/lifecycle-hooks) chapter.
~~~
This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
{@example 'cb-component-communication/ts/src/app/version-child.component.ts'}
<code-example path="cb-component-communication/src/app/version-child.component.ts">
</code-example>
The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them.
{@example 'cb-component-communication/ts/src/app/version-parent.component.ts'}
<code-example path="cb-component-communication/src/app/version-parent.component.ts">
</code-example>
Here's the output of a button-pushing sequence:
@ -108,35 +124,41 @@ Here's the output of a button-pushing sequence:
### Test it
Test that ***both*** input properties are set initially and that button clicks trigger
Test that ***both*** input properties are set initially and that button clicks trigger
the expected `ngOnChanges` calls and values:
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child-onchanges'}
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges">
</code-example>
[Back to top](guide/component-communication#top)
<a id="child-to-parent"></a>## Parent listens for child event
The child component exposes an `EventEmitter` property with which it `emits` events when something happens.
The child component exposes an `EventEmitter` property with which it `emits` events when something happens.
The parent binds to that event property and reacts to those events.
The child's `EventEmitter` property is an ***output property***,
The child's `EventEmitter` property is an ***output property***,
typically adorned with an [@Output decoration](guide/template-syntax)
as seen in this `VoterComponent`:
{@example 'cb-component-communication/ts/src/app/voter.component.ts'}
<code-example path="cb-component-communication/src/app/voter.component.ts">
Clicking a button triggers emission of a `true` or `false` (the boolean *payload*).
</code-example>
The parent `VoteTakerComponent` binds an event handler (`onVoted`) that responds to the child event
payload (`$event`) and updates a counter.
Clicking a button triggers emission of a `true` or `false`, the boolean *payload*.
The parent `VoteTakerComponent` binds an event handler called `onVoted()` that responds to the child event
payload `$event` and updates a counter.
{@example 'cb-component-communication/ts/src/app/votetaker.component.ts'}
<code-example path="cb-component-communication/src/app/votetaker.component.ts">
The framework passes the event argument &mdash; represented by `$event` &mdash; to the handler method,
</code-example>
The framework passes the event argument&mdash;represented by `$event`&mdash;to the handler method,
and the method processes it:
<figure class='image-display'>
@ -148,39 +170,45 @@ and the method processes it:
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
{@example 'cb-component-communication/e2e-spec.ts' region='child-to-parent'}
<code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent">
</code-example>
[Back to top](guide/component-communication#top)
## Parent interacts with child via *local variable*
A parent component cannot use data binding to read child properties
or invoke child methods. We can do both
or invoke child methods. You can do both
by creating a template reference variable for the child element
and then reference that variable *within the parent template*
as seen in the following example.
<a id="countdown-timer-example"></a>
We have a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket.
The following is a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket.
It has `start` and `stop` methods that control the clock and it displays a
countdown status message in its own template.
{@example 'cb-component-communication/ts/src/app/countdown-timer.component.ts'}
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts">
Let's see the `CountdownLocalVarParentComponent` that hosts the timer component.
</code-example>
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
{@example 'cb-component-communication/ts/src/app/countdown-parent.component.ts' region='lv'}
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv">
The parent component cannot data bind to the child's
</code-example>
The parent component cannot data bind to the child's
`start` and `stop` methods nor to its `seconds` property.
We can place a local variable (`#timer`) on the tag (`<countdown-timer>`) representing the child component.
That gives us a reference to the child component itself and the ability to access
You can place a local variable, `#timer`, on the tag `<countdown-timer>` representing the child component.
That gives you a reference to the child component and the ability to access
*any of its properties or methods* from within the parent template.
In this example, we wire parent buttons to the child's `start` and `stop` and
use interpolation to display the child's `seconds` property.
This example wires parent buttons to the child's `start` and `stop` and
uses interpolation to display the child's `seconds` property.
Here we see the parent and child working together.
@ -198,53 +226,65 @@ match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer:
{@example 'cb-component-communication/e2e-spec.ts' region='countdown-timer-tests'}
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests">
</code-example>
[Back to top](guide/component-communication#top)
<a id="parent-to-view-child"></a>## Parent calls a *ViewChild*
<a id="parent-to-view-child"></a>## Parent calls an _@ViewChild()_
The *local variable* approach is simple and easy. But it is limited because
The *local variable* approach is simple and easy. But it is limited because
the parent-child wiring must be done entirely within the parent template.
The parent component *itself* has no access to the child.
We can't use the *local variable* technique if an instance of the parent component *class*
You can't use the *local variable* technique if an instance of the parent component *class*
must read or write child component values or must call child component methods.
When the parent component *class* requires that kind of access,
we ***inject*** the child component into the parent as a *ViewChild*.
When the parent component *class* requires that kind of access,
***inject*** the child component into the parent as a *ViewChild*.
We'll illustrate this technique with the same [Countdown Timer](guide/component-communication#countdown-timer-example) example.
We won't change its appearance or behavior.
The following example illustrates this technique with the same
[Countdown Timer](guide/component-communication#countdown-timer-example) example.
Neither its appearance nor its behavior will change.
The child [CountdownTimerComponent](guide/component-communication#countdown-timer-example) is the same as well.
We are switching from the *local variable* to the *ViewChild* technique
solely for the purpose of demonstration.Here is the parent, `CountdownViewChildParentComponent`:
{@example 'cb-component-communication/ts/src/app/countdown-parent.component.ts' region='vc'}
~~~ {.l-sub-section}
The switch from the *local variable* to the *ViewChild* technique
is solely for the purpose of demonstration.
~~~
Here is the parent, `CountdownViewChildParentComponent`:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc">
</code-example>
It takes a bit more work to get the child view into the parent component *class*.
We import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook.
We inject the child `CountdownTimerComponent` into the private `timerComponent` property
First, you have to import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook.
Next, inject the child `CountdownTimerComponent` into the private `timerComponent` property
via the `@ViewChild` property decoration.
The `#timer` local variable is gone from the component metadata.
Instead we bind the buttons to the parent component's own `start` and `stop` methods and
The `#timer` local variable is gone from the component metadata.
Instead, bind the buttons to the parent component's own `start` and `stop` methods and
present the ticking seconds in an interpolation around the parent component's `seconds` method.
These methods access the injected timer component directly.
The `ngAfterViewInit` lifecycle hook is an important wrinkle.
The `ngAfterViewInit()` lifecycle hook is an important wrinkle.
The timer component isn't available until *after* Angular displays the parent view.
So we display `0` seconds initially.
So it displays `0` seconds initially.
Then Angular calls the `ngAfterViewInit` lifecycle hook at which time it is *too late*
to update the parent view's display of the countdown seconds.
Angular's unidirectional data flow rule prevents us from updating the parent view's
in the same cycle. We have to *wait one turn* before we can display the seconds.
Angular's unidirectional data flow rule prevents updating the parent view's
in the same cycle. The app has to *wait one turn* before it can display the seconds.
We use `setTimeout` to wait one tick and then revise the `seconds` method so
Use `setTimeout()` to wait one tick and then revise the `seconds()` method so
that it takes future values from the timer component.
### Test it
@ -255,34 +295,47 @@ Use [the same countdown timer tests](guide/component-communication#countdown-tes
A parent component and its children share a service whose interface enables bi-directional communication
*within the family*.
The scope of the service instance is the parent component and its children.
The scope of the service instance is the parent component and its children.
Components outside this component subtree have no access to the service or their communications.
This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children.
{@example 'cb-component-communication/ts/src/app/mission.service.ts'}
<code-example path="cb-component-communication/src/app/mission.service.ts">
</code-example>
The `MissionControlComponent` both provides the instance of the service that it shares with its children
(through the `providers` metadata array) and injects that instance into itself through its constructor:
{@example 'cb-component-communication/ts/src/app/missioncontrol.component.ts'}
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts">
</code-example>
The `AstronautComponent` also injects the service in its constructor.
Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance:
{@example 'cb-component-communication/ts/src/app/astronaut.component.ts'}
<code-example path="cb-component-communication/src/app/astronaut.component.ts">
</code-example>
Notice that we capture the `subscription` and unsubscribe when the `AstronautComponent` is destroyed.
~~~ {.l-sub-section}
Notice that this example captures the `subscription` and `unsubscribe()` when the `AstronautComponent` is destroyed.
This is a memory-leak guard step. There is no actual risk in this app because the
lifetime of a `AstronautComponent` is the same as the lifetime of the app itself.
That *would not* always be true in a more complex application.
We do not add this guard to the `MissionControlComponent` because, as the parent,
it controls the lifetime of the `MissionService`.The *History* log demonstrates that messages travel in both directions between
You don't add this guard to the `MissionControlComponent` because, as the parent,
it controls the lifetime of the `MissionService`.
~~~
The *History* log demonstrates that messages travel in both directions between
the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service:
@ -293,9 +346,11 @@ facilitated by the service:
### Test it
Tests click buttons of both the parent `MissionControlComponent` and the `AstronautComponent` children
and verify that the *History* meets expectations:
and verify that the history meets expectations:
{@example 'cb-component-communication/e2e-spec.ts' region='bidirectional-service'}
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service">
</code-example>
[Back to top](guide/component-communication#top)

View File

@ -1,194 +0,0 @@
@title
Component-relative Paths
@intro
Use relative URLs for component templates and styles.
@description
## Write *Component-Relative* URLs to component templates and style files
Our components often refer to external template and style files.
We identify those files with a URL in the `templateUrl` and `styleUrls` properties of the `@Component` metadata
as seen here:
{@example 'cb-component-relative-paths/ts/src/app/some.component.ts' region='absolute-config'}
By default, we *must* specify the full path back to the application root.
We call this an ***absolute path*** because it is *absolute* with respect to the application root.
There are two problems with an *absolute path*:
1. We have to remember the full path back to the application root.
1. We have to update the URL when we move the component around in the application files structure.
It would be much easier to write and maintain our application components if we could specify template and style locations
*relative* to their component class file.
*We can!*
~~~ {.alert.is-important}
We can if we build our application as `commonjs` modules and load those modules
with a suitable package loader such as `systemjs` or `webpack`.
Learn why [below](guide/component-relative-paths#why-default).
The Angular CLI uses these technologies and defaults to the
*component-relative path* approach described here.
CLI users can skip this chapter or read on to understand
how it works.
~~~
## _Component-Relative_ Paths
Our goal is to specify template and style URLs *relative* to their component class files,
hence the term ***component-relative path***.
The key to success is following a convention that puts related component files in well-known locations.
We recommend keeping component template and component-specific style files as *siblings* of their
companion component class files.
Here we see the three files for `SomeComponent` sitting next to each other in the `app` folder.
<aio-filetree>
<aio-folder>
app
<aio-file>
some.component.css
</aio-file>
<aio-file>
some.component.html
</aio-file>
<aio-file>
some.component.ts
</aio-file>
</aio-folder>
</aio-filetree>
We'll have more files and folders &mdash; and greater folder depth &mdash; as our application grows.
We'll be fine as long as the component files travel together as the inseparable siblings they are.
### Set the *moduleId*
Having adopted this file structure convention, we can specify locations of the template and style files
relative to the component class file simply by setting the `moduleId` property of the `@Component` metadata like this
{@example 'cb-component-relative-paths/ts/src/app/some.component.ts' region='module-id'}
We strip the `src/app/` base path from the `templateUrl` and `styleUrls` and replace it with `./`.
The result looks like this:
{@example 'cb-component-relative-paths/ts/src/app/some.component.ts' region='relative-config'}
~~~ {.alert.is-helpful}
Webpack users may prefer [an alternative approach](guide/component-relative-paths#webpack).
~~~
## Source
**We can see the <live-example name="cb-component-relative-paths"></live-example>**
and download the source code from there
or simply read the pertinent source here.
<md-tab-group>
<md-tab label="src/app/some.component.ts">
{@example 'cb-component-relative-paths/ts/src/app/some.component.ts'}
</md-tab>
<md-tab label="src/app/some.component.html">
{@example 'cb-component-relative-paths/ts/src/app/some.component.html'}
</md-tab>
<md-tab label="src/app/some.component.css">
{@example 'cb-component-relative-paths/ts/src/app/some.component.css'}
</md-tab>
<md-tab label="src/app/app.component.ts">
{@example 'cb-component-relative-paths/ts/src/app/app.component.ts'}
</md-tab>
</md-tab-group>
{@a why-default}
## Appendix: why *component-relative* is not the default
A *component-relative* path is obviously superior to an *absolute* path.
Why did Angular default to the *absolute* path?
Why do *we* have to set the `moduleId`? Why can't Angular set it?
First, let's look at what happens if we use a relative path and omit the `moduleId`.
`EXCEPTION: Failed to load some.component.html`
Angular can't find the file so it throws an error.
Why can't Angular calculate the template and style URLs from the component file's location?
Because the location of the component can't be determined without the developer's help.
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
from CommonJS packages, to name a few.
We might generate modules in any of several formats.
We might not be writing modular code at all!
With this diversity of packaging and module load strategies,
it's not possible for Angular to know with certainty where these files reside at runtime.
The only location Angular can be sure of is the URL of the `index.html` home page, the application root.
So by default it resolves template and style paths relative to the URL of `index.html`.
That's why we previously wrote our file URLs with an `app/` base path prefix.
But *if* we follow the recommended guidelines and we write modules in `commonjs` format
and we use a module loader that *plays nice*,
*then* we &mdash; the developers of the application &mdash;
know that the semi-global `module.id` variable is available and contains
the absolute URL of the component class module file.
That knowledge enables us to tell Angular where the *component* file is
by setting the `moduleId`:
{@example 'cb-component-relative-paths/ts/src/app/some.component.ts' region='module-id'}
{@a webpack}
## Webpack: load templates and styles
Webpack developers have an alternative to `moduleId`.
They can load templates and styles at runtime by adding `./` at the beginning of the `template` and `styles` / `styleUrls`
properties that reference *component-relative URLS.
{@example 'webpack/ts/src/app/app.component.ts'}
Webpack will do a `require` behind the scenes to load the templates and styles. Read more [here](guide/webpack).
See the [Introduction to Webpack](guide/webpack).

View File

@ -5,6 +5,7 @@ Component Styles
Learn how to apply CSS styles to components.
@description
Angular applications are styled with standard CSS. That means you can apply
everything you know about CSS stylesheets, selectors, rules, and media queries
directly to Angular applications.
@ -36,7 +37,9 @@ The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
Usually you give it one string, as in the following example:
{@example 'component-styles/ts/src/app/hero-app.component.ts'}
<code-example path="component-styles/src/app/hero-app.component.ts" linenums="false">
</code-example>
The selectors you put into a component's styles apply only within the template
of that component. The `h1` selector in the preceding example applies only to the `<h1>` tag
@ -70,7 +73,9 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template).
{@example 'component-styles/ts/src/app/hero-details.component.css' region='host'}
<code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false">
</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
@ -82,7 +87,9 @@ 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.
{@example 'component-styles/ts/src/app/hero-details.component.css' region='hostfunction'}
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false">
</code-example>
### :host-context
@ -98,7 +105,9 @@ The following example applies a `background-color` style to all `<h2>` elements
if some ancestor element has the CSS class `theme-light`.
{@example 'component-styles/ts/src/app/hero-details.component.css' region='hostcontext'}
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false">
</code-example>
### /deep/
@ -111,7 +120,9 @@ children and content children of the component.
The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM.
{@example 'component-styles/ts/src/app/hero-details.component.css' region='deep'}
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false">
</code-example>
The `/deep/` selector also has the alias `>>>`. You can use either interchangeably.
@ -144,7 +155,9 @@ You can add a `styles` #{_array} property to the `@Component` #{_decorator}.
Each string in the #{_array} (usually just one string) defines the CSS.
{@example 'component-styles/ts/src/app/hero-app.component.ts'}
<code-example path="component-styles/src/app/hero-app.component.ts">
</code-example>
### Style URLs in metadata
@ -152,7 +165,42 @@ You can load styles from external CSS files by adding a `styleUrls` attribute
into a component's `@Component` #{_decorator}:
{@example 'component-styles/ts/src/app/hero-details.component.ts' region='styleurls'}
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls">
</code-example>
~~~ {.alert.is-important}
The URL is relative to the *application root*, which is usually the
location of the `index.html` web page that hosts the application.
The style file URL is *not* relative to the component file.
That's why the example URL begins `src/app/`.
To specify a URL relative to the component file, see [Appendix 2](guide/component-styles#relative-urls).
~~~
~~~ {.l-sub-section}
If you use module bundlers like Webpack, you can also use the `styles` attribute
to load styles from external files at build time. You could write:
`styles: [require('my.component.css')]`
Set the `styles` property, not the `styleUrls` property. The module
bundler loads the CSS strings, not Angular.
Angular sees the CSS strings only after the bundler loads them.
To Angular, it's as if you wrote the `styles` array by hand.
For information on loading CSS in this manner, refer to the module bundler's documentation.
~~~
### Template inline styles
@ -160,7 +208,9 @@ You can embed styles directly into the HTML template by putting them
inside `<style>` tags.
{@example 'component-styles/ts/src/app/hero-controls.component.ts' region='inlinestyles'}
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles">
</code-example>
### Template link tags
@ -170,7 +220,9 @@ As with `styleUrls`, the link tag's `href` URL is relative to the
application root, not the component file.
{@example 'component-styles/ts/src/app/hero-team.component.ts' region='stylelink'}
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink">
</code-example>
### CSS @imports
@ -178,8 +230,12 @@ You can also import CSS files into the CSS files using the standard CSS `@import
For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import)
on the [MDN](https://developer.mozilla.org) site.
In this case, the URL is relative to the CSS file into which you're importing.
{@example 'component-styles/ts/src/app/hero-details.component.css' region='import'}
<code-example path="component-styles/src/app/hero-details.component.css" region="import">
</code-example>
@ -210,7 +266,9 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
{@example 'component-styles/ts/src/app/quest-summary.component.ts' region='encapsulation.native'}
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false">
</code-example>
`Native` view encapsulation only works on browsers that have native support
for shadow DOM (see [Shadow DOM v0](http://caniuse.com/#feat=shadowdom) on the
@ -283,3 +341,11 @@ It's common practice to split a component's code, HTML, and CSS into three separ
You include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
Because these files are co-located with the component,
it would be nice to refer to them by name without also having to specify a path back to the root of the application.
You can use a relative URL by prefixing your filenames with `./`:
<code-example path="component-styles/src/app/quest-summary.component.ts">
</code-example>

File diff suppressed because it is too large Load Diff

View File

@ -108,12 +108,16 @@ Load the few files you need from the web instead.
with versions that load from the web. It might look like this.
{@example 'deployment/ts/src/index.html' region='node-module-scripts'}
<code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false">
</code-example>
(2) Replace the `systemjs.config.js` script with a script that
loads `systemjs.config.server.js`.
{@example 'deployment/ts/src/index.html' region='systemjs-config'}
<code-example path="deployment/src/index.html" region="systemjs-config" linenums="false">
</code-example>
(3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder.
This alternative version configures _SystemJS_ to load _UMD_ versions of Angular
@ -125,7 +129,9 @@ you make to `systemjs.config.js`.
Notice the `paths` key:
{@example 'deployment/ts/src/systemjs.config.server.js' region='paths'}
<code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false">
</code-example>
In the standard SystemJS config, the `npm` path points to the `node_modules/`.
In this server config, it points to
@ -143,44 +149,45 @@ Then change the config's `'npm'` path to point to that folder.
The following trivial router sample app shows these changes.
<md-tab-group>
<md-tab label="index.html">
{@example 'deployment/ts/src/index.html'}
</md-tab>
<code-tabs>
<code-pane title="index.html" path="deployment/src/index.html">
</code-pane>
<md-tab label="systemjs.config.server.js">
{@example 'deployment/ts/src/systemjs.config.server.js'}
</md-tab>
<code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js">
</code-pane>
<md-tab label="main.ts">
{@example 'deployment/ts/src/main.ts'}
</md-tab>
<code-pane title="main.ts" path="deployment/src/main.ts">
</code-pane>
<md-tab label="app/app.module.ts">
{@example 'deployment/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts">
</code-pane>
<md-tab label="app/app.component.ts">
{@example 'deployment/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts">
</code-pane>
<md-tab label="app/crisis-list.component.ts">
{@example 'deployment/ts/src/app/crisis-list.component.ts'}
</md-tab>
<code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts">
</code-pane>
<md-tab label="app/hero-list.component.ts">
{@example 'deployment/ts/src/app/hero-list.component.ts'}
</md-tab>
<code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Practice with this sample before attempting these techniques on your application.
@ -338,7 +345,14 @@ For example, given the `<base href="/my/app/">`, the browser resolves a URL such
into a server request for `my/app/some/place/foo.jpg`.
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let) alternative.In development, you typically start the server in the folder that holds `index.html`.
~~~ {.l-sub-section}
See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let) alternative.
~~~
In development, you typically start the server in the folder that holds `index.html`.
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
But on the shared or production server, you might serve the app from a subfolder.
@ -364,7 +378,9 @@ Switching to production mode can make it run faster by disabling development spe
To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`.
{@example 'deployment/ts/src/main.ts' region='enableProdMode'}
<code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false">
</code-example>

View File

@ -5,6 +5,7 @@ Displaying Data
Property binding helps show app data in the UI.
@description
You can display data by binding controls in an HTML template to properties of an Angular component.
In this page, you'll create a component with a list of heroes.
@ -23,9 +24,16 @@ The final UI looks like this:
* [Showing !{_an} !{_array} property with NgFor](guide/displaying-data#ngFor).
* [Conditional display with NgIf](guide/displaying-data#ngIf).
~~~ {.l-sub-section}
The <live-example></live-example> demonstrates all of the syntax and code
snippets described in this page.
~~~
## Showing component properties with interpolation
The easiest way to display a component property
is to bind the property name through interpolation.
@ -40,23 +48,44 @@ changing the template and the body of the component.
When you're done, it should look like this:
{@example 'displaying-data/ts/src/app/app.component.1.ts'}
<code-example path="displaying-data/src/app/app.component.1.ts">
</code-example>
You added two properties to the formerly empty component: `title` and `myHero`.
The revised template displays the two component properties using double curly brace
interpolation:
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example>
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
inserts those values into the browser. Angular updates the display
when these properties change.
~~~ {.l-sub-section}
More precisely, the redisplay occurs after some kind of asynchronous event related to
the view, such as a keystroke, a timer completion, or a response to an HTTP request.
~~~
Notice that you don't call **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for you. How?
The CSS `selector` in the `@Component` !{_decorator} specifies an element named `<my-app>`.
That element is a placeholder in the body of your `index.html` file:
<code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body">
</code-example>
When you bootstrap with the `AppComponent` class (in <ngio-ex path="main.ts"></ngio-ex>), Angular looks for a `<my-app>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag.
@ -83,13 +112,31 @@ In either style, the template data bindings have the same access to the componen
## Showing !{_an} !{_array} property with ***ngFor**
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" title="src/app/app.component.ts (class)" region="class">
</code-example>
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" title="src/app/app.component.ts (template)" region="template">
</code-example>
This UI uses the HTML unordered list with `<ul>` and `<li>` tags. The `*ngFor`
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" title="src/app/app.component.ts (li)" region="li">
</code-example>
~~~ {.alert.is-important}
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
@ -107,8 +154,15 @@ Angular duplicates the `<li>` for each item in the list, setting the `hero` vari
to the item (the hero) in the current iteration. Angular uses that variable as the
context for the interpolation in the double curly braces.
~~~ {.l-sub-section}
In this case, `ngFor` is displaying !{_an} !{_array}, but `ngFor` can
repeat items for any [iterable](guide/!{_iterableUrl}) object.Now the heroes appear in an unordered list.
repeat items for any [iterable](guide/!{_iterableUrl}) object.
~~~
Now the heroes appear in an unordered list.
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
@ -128,13 +182,48 @@ of hero names into !{_an} !{_array} of `Hero` objects. For that you'll need a `H
Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)">
</code-example>
You've defined a class with a constructor and two properties: `id` and `name`.
It might not look like the class has properties, but it does.
The declaration of the constructor parameters takes advantage of a TypeScript shortcut.
Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id">
</code-example>
That brief syntax does a lot:
* Declares a constructor parameter and its type.
* Declares a public property of the same name.
* Initializes that property with the corresponding argument when creating an instance of the class.
## Using the Hero class
After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ !{_array}
of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes">
</code-example>
Next, update the template.
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" title="src/app/app.component.ts (template)" region="template">
</code-example>
The display looks the same, but the code is clearer.
## Conditional display with NgIf
@ -147,6 +236,12 @@ The Angular `ngIf` directive inserts or removes an element based on a !{_boolean
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" title="src/app/app.component.ts (message)" region="message">
</code-example>
~~~ {.alert.is-important}
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
@ -184,3 +279,30 @@ Now you know how to use:
- **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
Here's the final code:
<code-tabs>
<code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
</code-pane>
<code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
</code-pane>
<code-pane title="main.ts" path="displaying-data/src/main.ts">
</code-pane>
</code-tabs>

View File

@ -37,7 +37,9 @@ Before components can be added we have to define an anchor point to mark where c
The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template.
{@example 'cb-dynamic-component-loader/ts/src/app/ad.directive.ts'}
<code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" linenums="false">
</code-example>
`AdDirective` injects `ViewContainerRef` to gain access to the view container of the element that will become the host of the dynamically added component.
@ -47,41 +49,44 @@ The next step is to implement the ad banner. Most of the implementation is in `A
We start by adding a `template` element with the `AdDirective` directive applied.
<md-tab-group>
<md-tab label="ad-banner.component.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="ad-banner.component.ts" path="cb-dynamic-component-loader/src/app/ad-banner.component.ts">
</code-pane>
<md-tab label="ad.service.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/ad.service.ts'}
</md-tab>
<code-pane title="ad.service.ts" path="cb-dynamic-component-loader/src/app/ad.service.ts">
</code-pane>
<md-tab label="ad-item.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/ad-item.ts'}
</md-tab>
<code-pane title="ad-item.ts" path="cb-dynamic-component-loader/src/app/ad-item.ts">
</code-pane>
<md-tab label="app.module.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="app.module.ts" path="cb-dynamic-component-loader/src/app/app.module.ts">
</code-pane>
<md-tab label="app.component">
{@example 'cb-dynamic-component-loader/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="app.component" path="cb-dynamic-component-loader/src/app/app.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
The `template` element decorated with the `ad-host` directive marks where dynamically loaded components will be added.
Using a `template` element is recommended since it doesn't render any additional output.
{@example 'cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts' region='ad-host'}
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" linenums="false">
</code-example>
### Resolving Components
@ -102,7 +107,9 @@ Generally the compiler will generate a component factory for any component refer
With dynamically loaded components there are no selector references in the templates since components are loaded at runtime. In order to ensure that the compiler will still generate a factory, dynamically loaded components have to be added to their `NgModule`'s `entryComponents` array.
{@example 'cb-dynamic-component-loader/ts/src/app/app.module.ts' region='entry-components'}
<code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" linenums="false">
</code-example>
Components are added to the template by calling `createComponent` on the `ViewContainerRef` reference.
@ -112,24 +119,25 @@ In the Ad banner, all components implement a common `AdComponent` interface to s
Two sample components and the `AdComponent` interface are shown below:
<md-tab-group>
<md-tab label="hero-job-ad.component.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/hero-job-ad.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="hero-job-ad.component.ts" path="cb-dynamic-component-loader/src/app/hero-job-ad.component.ts">
</code-pane>
<md-tab label="hero-profile.component.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/hero-profile.component.ts'}
</md-tab>
<code-pane title="hero-profile.component.ts" path="cb-dynamic-component-loader/src/app/hero-profile.component.ts">
</code-pane>
<md-tab label="ad.component.ts">
{@example 'cb-dynamic-component-loader/ts/src/app/ad.component.ts'}
</md-tab>
<code-pane title="ad.component.ts" path="cb-dynamic-component-loader/src/app/ad.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
The final ad banner looks like this:
<figure class='image-display'>

View File

@ -42,19 +42,20 @@ Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, s
We bootstrap our `AppModule` in main.ts.
<md-tab-group>
<md-tab label="app.module.ts">
{@example 'cb-dynamic-form/ts/src/app/app.module.ts'}
</md-tab>
<code-tabs>
<code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts">
</code-pane>
<md-tab label="main.ts">
{@example 'cb-dynamic-form/ts/src/main.ts'}
</md-tab>
<code-pane title="main.ts" path="cb-dynamic-form/src/main.ts">
</code-pane>
</md-tab-group>
</code-tabs>
<a id="object-model"></a>## Question Model
@ -66,7 +67,9 @@ The "question" is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class.
{@example 'cb-dynamic-form/ts/src/app/question-base.ts'}
<code-example path="cb-dynamic-form/src/app/question-base.ts">
</code-example>
From this base we derived two new classes in `TextboxQuestion` and `DropdownQuestion` that represent Textbox and Dropdown questions.
The idea is that the form will be bound to specific question types and render the appropriate controls dynamically.
@ -74,53 +77,61 @@ The idea is that the form will be bound to specific question types and render th
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property.
{@example 'cb-dynamic-form/ts/src/app/question-textbox.ts'}
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false">
</code-example>
`DropdownQuestion` presents a list of choices in a select box.
{@example 'cb-dynamic-form/ts/src/app/question-dropdown.ts'}
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false">
</code-example>
Next we have defined `QuestionControlService`, a simple service for transforming our questions to a `FormGroup`.
In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules.
{@example 'cb-dynamic-form/ts/src/app/question-control.service.ts'}
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false">
</code-example>
<a id="form-component"></a>## Question form components
Now that we have defined the complete model we are ready to create components to represent the dynamic form.
`DynamicFormComponent` is the entry point and the main container for the form.
<md-tab-group>
<md-tab label="dynamic-form.component.html">
{@example 'cb-dynamic-form/ts/src/app/dynamic-form.component.html'}
</md-tab>
<code-tabs>
<code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html">
</code-pane>
<md-tab label="dynamic-form.component.ts">
{@example 'cb-dynamic-form/ts/src/app/dynamic-form.component.ts'}
</md-tab>
<code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
It presents a list of questions, each question bound to a `<df-question>` component element.
The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object.
<md-tab-group>
<md-tab label="dynamic-form-question.component.html">
{@example 'cb-dynamic-form/ts/src/app/dynamic-form-question.component.html'}
</md-tab>
<code-tabs>
<code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html">
</code-pane>
<md-tab label="dynamic-form-question.component.ts">
{@example 'cb-dynamic-form/ts/src/app/dynamic-form-question.component.ts'}
</md-tab>
<code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Notice this component can present any type of question in our model.
We only have two types of questions at this point but we can imagine many more.
@ -139,12 +150,16 @@ underlying control objects, populated from the question model with display and v
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array.
{@example 'cb-dynamic-form/ts/src/app/question.service.ts'}
<code-example path="cb-dynamic-form/src/app/question.service.ts">
</code-example>
Finally, we display an instance of the form in the `AppComponent` shell.
{@example 'cb-dynamic-form/ts/src/app/app.component.ts'}
<code-example path="cb-dynamic-form/src/app/app.component.ts">
</code-example>
<a id="dynamic-template"></a>## Dynamic Template
Although in this example we're modelling a job application for heroes, there are no references to any specific hero question

View File

@ -8,29 +8,42 @@ Validate user's form entries.
{@a top}
We can improve overall data quality by validating user input for accuracy and completeness.
Improve overall data quality by validating user input for accuracy and completeness.
In this cookbook we show how to validate user input in the UI and display useful validation messages
This cookbook shows how to validate user input in the UI and display useful validation messages
using first the template-driven forms and then the reactive forms approach.
Learn more about these choices in the [Forms chapter.](guide/forms)
~~~ {.l-sub-section}
Read more about these choices in the [Forms](guide/forms)
and the [Reactive Forms](guide/reactive-forms) guides.
~~~
{@a toc}
## Table of Contents
## Contents
[Simple Template-Driven Forms](guide/form-validation#template1)
[Template-Driven Forms with validation messages in code](guide/form-validation#template2)
[Reactive Forms with validation in code](guide/form-validation#reactive)
[Custom validation](guide/form-validation#custom-validation)
[Testing](guide/form-validation#testing)
* [Simple template-driven forms](guide/form-validation#template1)
* [Template-driven forms with validation messages in code](guide/form-validation#template2)
- [Component Class](guide/form-validation#component-class)
- [The benefits of messages in code](guide/form-validation#improvement)
- [`FormModule` and template-driven forms](guide/form-validation#formmodule)
* [Reactive forms with validation in code](guide/form-validation#reactive)
- [Switch to the `ReactiveFormsModule`](guide/form-validation#reactive-forms-module)
- [Component template](guide/form-validation#reactive-component-template)
- [Component class](guide/form-validation#reactive-component-class)
- [`FormBuilder` declaration](guide/form-validation#formbuilder)
- [Committing hero value changes](guide/form-validation#committing-changes)
* [Custom validation](guide/form-validation#custom-validation)
- [Custom validation directive](guide/form-validation#custom-validation-directive)
* [Testing considerations](guide/form-validation#testing)
{@a live-example}
**Try the live example to see and download the full cookbook source code**
**Try the live example to see and download the full cookbook source code.**
<live-example name="cb-form-validation" embedded=true img="cookbooks/form-validation/plunker.png">
</live-example>
@ -39,14 +52,14 @@ Learn more about these choices in the [Forms chapter.](guide/forms)
{@a template1}
## Simple Template-Driven Forms
## Simple template-driven forms
In the template-driven approach, you arrange
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
You add Angular form directives (mostly directives beginning `ng...`) to help
Angular construct a corresponding internal control model that implements form functionality.
We say that the control model is _implicit_ in the template.
In template-drive forms, the control model is _implicit_ in the template.
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
to the elements. Angular interprets those as well, adding validator functions to the control model.
@ -54,139 +67,167 @@ to the elements. Angular interprets those as well, adding validator functions to
Angular exposes information about the state of the controls including
whether the user has "touched" the control or made changes and if the control values are valid.
In the first template validation example,
we add more HTML to read that control state and update the display appropriately.
Here's an excerpt from the template html for a single input box control bound to the hero name:
In this first template validation example,
notice the HTML that reads the control state and updates the display appropriately.
Here's an excerpt from the template HTML for a single input control bound to the hero name:
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.html' region='name-with-error-msg'}
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" linenums="false">
</code-example>
Note the following:
- The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
- We set the `name` attribute of the input box to `"name"` so Angular can track this input element and associate it
- The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
with an Angular form control called `name` in its internal control model.
- We use the `[(ngModel)]` directive to two-way data bind the input box to the `hero.name` property.
- The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
- We set a template variable (`#name`) to the value `"ngModel"` (always `ngModel`).
This gives us a reference to the Angular `NgModel` directive
associated with this control that we can use _in the template_
- The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
This gives you a reference to the Angular `NgModel` directive
associated with this control that you can use _in the template_
to check for control states such as `valid` and `dirty`.
- The `*ngIf` on `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
- The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
the control is either `dirty` or `touched`.
- Each nested `<div>` can present a custom message for one of the possible validation errors.
We've prepared messages for `required`, `minlength`, and `maxlength`.
There are messages for `required`, `minlength`, and `maxlength`.
The full template repeats this kind of layout for each data entry control on the form.
{@a why-check}
~~~ {.l-sub-section}
#### Why check _dirty_ and _touched_?
We shouldn't show errors for a new hero before the user has had a chance to edit the value.
The app shouldn't show errors for a new hero before the user has had a chance to edit the value.
The checks for `dirty` and `touched` prevent premature display of errors.
Learn about `dirty` and `touched` in the [Forms](guide/forms) chapter.The component class manages the hero model used in the data binding
Learn about `dirty` and `touched` in the [Forms](guide/forms) guide.
~~~
The component class manages the hero model used in the data binding
as well as other code to support the view.
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.ts' region='class'}
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class">
</code-example>
Use this template-driven validation technique when working with static forms with simple, standard validation rules.
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach:
<md-tab-group>
<md-tab label="template/hero-form-template1.component.html">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.html'}
</md-tab>
<code-tabs>
<code-pane title="template/hero-form-template1.component.html" path="cb-form-validation/src/app/template/hero-form-template1.component.html">
</code-pane>
<md-tab label="template/hero-form-template1.component.ts">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.ts'}
</md-tab>
<code-pane title="template/hero-form-template1.component.ts" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
{@a template2}
## Template-Driven Forms with validation messages in code
## Template-driven forms with validation messages in code
While the layout is straightforward,
there are obvious shortcomings with the way we handle validation messages:
there are obvious shortcomings with the way it's handling validation messages:
* It takes a lot of HTML to represent all possible error conditions.
This gets out of hand when there are many controls and many validation rules.
* We're not fond of so much JavaScript logic in HTML.
* There's a lot of JavaScript logic in the HTML.
* The messages are static strings, hard-coded into the template.
We often require dynamic messages that we should shape in code.
It's easier to maintain _dynamic_ messages in the component class.
We can move the logic and the messages into the component with a few changes to
In this example, you can move the logic and the messages into the component with a few changes to
the template and component.
Here's the hero name again, excerpted from the revised template ("Template 2"), next to the original version:
<md-tab-group>
Here's the hero name again, excerpted from the revised template
(Template 2), next to the original version:
<md-tab label="hero-form-template2.component.html (name #2)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.html' region='name-with-error-msg'}
</md-tab>
<code-tabs>
<code-pane title="hero-form-template2.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg">
</code-pane>
<md-tab label="hero-form-template1.component.html (name #1)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.html' region='name-with-error-msg'}
</md-tab>
<code-pane title="hero-form-template1.component.html (name #1)" path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg">
</code-pane>
</md-tab-group>
</code-tabs>
The `<input>` element HTML is almost the same. There are noteworthy differences:
- The hard-code error message `<divs>` are gone.
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
It invalidates the control if the user enters "bob" anywhere in the name ([try it](guide/form-validation#live-example)).
We discuss [custom validation directives](guide/form-validation#custom-validation) later in this cookbook.
It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)).
See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information
on custom validation directives.
- The `#name` template variable is gone because we no longer refer to the Angular control for this element.
- The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
- Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
- Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
#### Component class
The original component code stays the same.
We _added_ new code to acquire the Angular form control and compose error messages.
{@a component-class}
### Component class
The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code
necessary in Template 2's component class to acquire the Angular
form control and compose error messages.
The first step is to acquire the form control that Angular created from the template by querying for it.
Look back at the top of the component template where we set the
Look back at the top of the component template at the
`#heroForm` template variable in the `<form>` element:
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.html' region='form-tag'}
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="form-tag" linenums="false">
</code-example>
The `heroForm` variable is a reference to the control model that Angular derived from the template.
We tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' region='view-child'}
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child" linenums="false">
</code-example>
Some observations:
- Angular `@ViewChild` queries for a template variable when you pass it
the name of that variable as a string (`'heroForm'` in this case).
- The `heroForm` object changes several times during the life of the component, most notably when we add a new hero.
We'll have to re-inspect it periodically.
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes.
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks)
when anything changes in the view.
That's the right time to see if there's a new `heroForm` object.
- When there _is_ a new `heroForm` model, we subscribe to its `valueChanged` _Observable_ property.
The `onValueChanged` handler looks for validation errors after every user keystroke.
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
The `onValueChanged` handler looks for validation errors after every keystroke.
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' region='handler'}
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="handler" linenums="false">
</code-example>
The `onValueChanged` handler interprets user data entry.
The `data` object passed into the handler contains the current element values.
@ -196,26 +237,33 @@ The `formErrors` is a dictionary of the hero fields that have validation rules a
Only two hero properties have validation rules, `name` and `power`.
The messages are empty strings when the hero data are valid.
For each field, the handler
- clears the prior error message if any
- acquires the field's corresponding Angular form control
- if such a control exists _and_ its been changed ("dirty") _and_ its invalid ...
- the handler composes a consolidated error message for all of the control's errors.
For each field, the `onValueChanged` handler does the following:
- Clears the prior error message, if any.
- Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
We'll need some error messages of course, a set for each validated property, one message per validation rule:
Next, the component needs some error messages of course&mdash;a set for each validated property with
one message per validation rule:
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' region='messages'}
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="messages" linenums="false">
</code-example>
Now every time the user makes a change, the `onValueChanged` handler checks for validation errors and produces messages accordingly.
### Is this an improvement?
{@a improvement}
### The benefits of messages in code
Clearly the template got substantially smaller while the component code got substantially larger.
It's not easy to see the benefit when there are just three fields and only two of them have validation rules.
Consider what happens as we increase the number of validated fields and rules.
Consider what happens as the number of validated
fields and rules increases.
In general, HTML is harder to read and maintain than code.
The initial template was already large and threatening to get rapidly worse as we add more validation message `<divs>`.
The initial template was already large and threatening to get rapidly worse
with the addition of more validation message `<div>` elements.
After moving the validation messaging to the component,
the template grows more slowly and proportionally.
@ -225,32 +273,44 @@ and one line per validation message.
Both trends are manageable.
Now that the messages are in code, we have more flexibility. We can compose messages more intelligently.
We can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
{@a formmodule}
### _FormModule_ and template-driven forms
Angular has two different forms modules &mdash; `FormsModule` and `ReactiveFormsModule` &mdash;
that correspond with the two approaches to form development.
Both modules come from the same `@angular/forms` library package.
Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come
from the same `@angular/forms` library package.
We've been reviewing the "Template-driven" approach which requires the `FormsModule`
Here's how we imported it in the `HeroFormTemplateModule`.
You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
Here's how you imported it in the `HeroFormTemplateModule`.
{@example 'cb-form-validation/ts/src/app/template/hero-form-template.module.ts'}
<code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" linenums="false">
</code-example>
We haven't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
~~~ {.l-sub-section}
This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
form template in this cookbook.
They're not germane to the validation story. Look at the [live example](guide/form-validation#live-example) if you're interested.
~~~
{@a reactive}
## Reactive Forms
## Reactive forms with validation in code
In the template-driven approach, you markup the template with form elements, validation attributes,
and `ng...` directives from the Angular `FormsModule`.
@ -263,101 +323,140 @@ At runtime, Angular binds the template elements to your control model based on y
This approach requires a bit more effort. *You have to write the control model and manage it*.
In return, you can
* add, change, and remove validation functions on the fly
* manipulate the control model dynamically from within the component
* [test](guide/form-validation#testing) validation and control logic with isolated unit tests.
This allows you to do the following:
* Add, change, and remove validation functions on the fly.
* Manipulate the control model dynamically from within the component.
* [Test](guide/form-validation#testing) validation and control logic with isolated unit tests.
The third cookbook sample re-writes the hero form in _reactive forms_ style.
The following cookbook sample re-writes the hero form in _reactive forms_ style.
{@a reactive-forms-module}
### Switch to the _ReactiveFormsModule_
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
The application module for the "Reactive Forms" feature in this sample looks like this:
The application module for the reactive forms feature in this sample looks like this:
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts'}
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.module.ts" linenums="false">
The "Reactive Forms" feature module and component are in the `src/app/reactive` folder.
Let's focus on the `HeroFormReactiveComponent` there, starting with its template.
</code-example>
The reactive forms feature module and component are in the `src/app/reactive` folder.
Focus on the `HeroFormReactiveComponent` there, starting with its template.
{@a reactive-component-template}
### Component template
We begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
to the `heroForm` property in the component class.
The `heroForm` is the control model that the component class builds and maintains.
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html' region='form-tag'}
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" linenums="false">
Then we modify the template HTML elements to match the _reactive forms_ style.
</code-example>
Next, modify the template HTML elements to match the _reactive forms_ style.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
<md-tab-group>
<md-tab label="hero-form-reactive.component.html (name #3)">
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html' region='name-with-error-msg'}
</md-tab>
<code-tabs>
<code-pane title="hero-form-reactive.component.html (name #3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg">
</code-pane>
<md-tab label="hero-form-template1.component.html (name #2)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.html' region='name-with-error-msg'}
</md-tab>
<code-pane title="hero-form-template1.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg">
</code-pane>
</md-tab-group>
</code-tabs>
Key changes:
- the validation attributes are gone (except `required`) because we'll be validating in code.
Key changes are:
- The validation attributes are gone (except `required`) because
validating happens in code.
- `required` remains, not for validation purposes (we'll cover that in the code),
- `required` remains, not for validation purposes (that's in the code),
but rather for css styling and accessibility.
~~~ {.l-sub-section}
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
(and perhaps the `aria-required` attribute) when the control has the `required` validator function.
Until then, apply the `required` attribute _and_ add the `Validator.required` function
to the control model, as we'll do below.
- the `formControlName` replaces the `name` attribute; it serves the same
purpose of correlating the input box with the Angular form control.
to the control model, as you'll see below.
- the two-way `[(ngModel)]` binding is gone.
~~~
- The `formControlName` replaces the `name` attribute; it serves the same
purpose of correlating the input with the Angular form control.
- The two-way `[(ngModel)]` binding is gone.
The reactive approach does not use data binding to move data into and out of the form controls.
We do that in code.
That's all in code.
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.### Component class
~~~ {.l-sub-section}
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
~~~
{@a reactive-component-class}
### Component class
The component class is now responsible for defining and managing the form control model.
Angular no longer derives the control model from the template so we can no longer query for it.
We create the Angular form control model explicitly with the help of the `FormBuilder`.
Angular no longer derives the control model from the template so you can no longer query for it.
You can create the Angular form control model explicitly with
the help of the `FormBuilder` class.
Here's the section of code devoted to that process, paired with the template-driven code it replaces:
<md-tab-group>
<md-tab label="reactive/hero-form-reactive.component.ts (FormBuilder)">
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' region='form-builder'}
</md-tab>
<code-tabs>
<code-pane title="reactive/hero-form-reactive.component.ts (FormBuilder)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="form-builder">
</code-pane>
<md-tab label="template/hero-form-template2.component.ts (ViewChild)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' region='view-child'}
</md-tab>
<code-pane title="template/hero-form-template2.component.ts (ViewChild)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child">
</code-pane>
</md-tab-group>
</code-tabs>
- we inject the `FormBuilder` in a constructor.
- Inject `FormBuilder` in a constructor.
- we call a `buildForm` method in the `ngOnInit` [lifecycle hook method](guide/lifecycle-hooks)
because that's when we'll have the hero data. We'll call it again in the `addHero` method.
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.- the `buildForm` method uses the `FormBuilder` (`fb`) to declare the form control model.
- Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](guide/lifecycle-hooks)
because that's when you'll have the hero data. Call it again in the `addHero` method.
~~~ {.l-sub-section}
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
~~~
- The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanged` event and calls it immediately
to the form's `valueChanges` event and calls it immediately
to set error messages for the new control model.
{@a formbuilder}
#### _FormBuilder_ declaration
The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
Each control spec is a control name with an array value.
The first array element is the current value of the corresponding hero field.
The (optional) second value is a validator function or an array of validator functions.
The optional second value is a validator function or an array of validator functions.
Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class.
Angular has stock validators that correspond to the standard HTML validation attributes.
@ -365,7 +464,17 @@ Angular has stock validators that correspond to the standard HTML validation att
The `forbiddenNames` validator on the `"name"` control is a custom validator,
discussed in a separate [section below](guide/form-validation#custom-validation).
~~~ {.l-sub-section}
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms) section of Reactive Forms guide.
~~~
{@a committing-changes}
#### Committing hero value changes
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
@ -373,57 +482,78 @@ Reactive forms do not use data binding to update data model properties.
The developer decides _when and how_ to update the data model from control values.
This sample updates the model twice:
1. when the user submits the form
1. when the user chooses to add a new hero
1. When the user submits the form.
1. When the user adds a new hero.
The `onSubmit` method simply replaces the `hero` object with the combined values of the form:
The `onSubmit()` method simply replaces the `hero` object with the combined values of the form:
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' region='on-submit'}
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" linenums="false">
</code-example>
This example is "lucky" in that the `heroForm.value` properties _just happen_ to
correspond _exactly_ to the hero data object properties.The `addHero` method discards pending changes and creates a brand new `hero` model object.
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' region='add-hero'}
~~~ {.l-sub-section}
Then it calls `buildForm` again which replaces the previous `heroForm` control model with a new one.
This example is lucky in that the `heroForm.value` properties _just happen_ to
correspond _exactly_ to the hero data object properties.
~~~
The `addHero()` method discards pending changes and creates a brand new `hero` model object.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" linenums="false">
</code-example>
Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one.
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
Here's the complete reactive component file, compared to the two template-driven component files.
<md-tab-group>
<md-tab label="reactive/hero-form-reactive.component.ts (#3)">
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="reactive/hero-form-reactive.component.ts (#3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts">
</code-pane>
<md-tab label="template/hero-form-template2.component.ts (#2)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.ts'}
</md-tab>
<code-pane title="template/hero-form-template2.component.ts (#2)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts">
</code-pane>
<md-tab label="template/hero-form-template1.component.ts (#1)">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template1.component.ts'}
</md-tab>
<code-pane title="template/hero-form-template1.component.ts (#1)" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves
~~~ {.l-sub-section}
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves,
and to compare all of the files in this cookbook sample.
~~~
{@a custom-validation}
## Custom validation
This cookbook sample has a custom `forbiddenNamevalidator` function that's applied to both the
This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the
template-driven and the reactive form controls. It's in the `src/app/shared` folder
and declared in the `SharedModule`.
Here's the `forbiddenNamevalidator` function itself:
Here's the `forbiddenNamevalidator()` function:
{@example 'cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' region='custom-validator'}
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" linenums="false">
</code-example>
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
and returns a validator function.
@ -432,43 +562,80 @@ In this sample, the forbidden name is "bob";
the validator rejects any hero name containing "bob".
Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
The `forbiddenNamevalidator` factory returns the configured validator function.
The `forbiddenNameValidator` factory returns the configured validator function.
That function takes an Angular control object and returns _either_
null if the control value is valid _or_ a validation error object.
The validation error object typically has a property whose name is the validation key ('forbiddenName')
and whose value is an arbitrary dictionary of values that we could insert into an error message (`{name}`).
The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
and whose value is an arbitrary dictionary of values that you could insert into an error message (`{name}`).
Learn more about validator functions in a _forthcoming_ chapter on custom form validation.#### Custom validation directive
In the reactive forms component we added a configured `forbiddenNamevalidator`
to the bottom of the `'name'` control's validator function list.
{@example 'cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' region='name-validators'}
{@a custom-validation-directive}
### Custom validation directive
In the reactive forms component, the `'name'` control's validator function list
has a `forbiddenNameValidator` at the bottom.
In the template-driven component template, we add the selector (`forbiddenName`) of a custom _attribute directive_ to the name's input box
and configured it to reject "bob".
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" linenums="false">
{@example 'cb-form-validation/ts/src/app/template/hero-form-template2.component.html' region='name-input'}
</code-example>
The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNamevalidator`.
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
of a custom _attribute directive_, which rejects "bob".
Angular forms recognizes the directive's role in the validation process because the directive registers itself
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-input" linenums="false">
</code-example>
The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`.
Angular `forms` 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 validation directives.
{@example 'cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' region='directive-providers'}
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" linenums="false">
The rest of the directive is unremarkable and we present it here without further comment.
</code-example>
{@example 'cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' region='directive'}
Here is the rest of the directive to help you get an idea of how it all comes together:
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive">
</code-example>
See the [Attribute Directives](guide/attribute-directives) chapter.
~~~ {.l-sub-section}
If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of
the `ForbiddenValidatorDirective`&mdash;the instance in the form with
its `forbiddenName` property bound to “bob". If you were to replace
`useExisting` with `useClass`, then youd be registering a new class instance, one that
doesnt have a `forbiddenName`.
To see this in action, run the example and then type “bob” in the name of Hero Form 2.
Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
This time, when you type “bob”, there's no "bob" error message.
~~~
~~~ {.l-sub-section}
For more information on attaching behavior to elements,
see [Attribute Directives](guide/attribute-directives).
~~~
{@a testing}
## Testing Considerations
We can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
_Isolated unit tests_ probe the component class directly, independent of its
interactions with its template, the DOM, other dependencies, or Angular itself.
@ -476,11 +643,12 @@ interactions with its template, the DOM, other dependencies, or Angular itself.
Such tests have minimal setup, are quick to write, and easy to maintain.
They do not require the `Angular TestBed` or asynchronous testing practices.
That's not possible with _Template-driven_ forms.
That's not possible with _template-driven_ forms.
The template-driven approach relies on Angular to produce the control model and
to derive validation rules from the HTML validation attributes.
You must use the `Angular TestBed` to create component test instances,
write asynchronous tests, and interact with the DOM.
While not difficult, this takes more time, work and skill &mdash;
factors that tend to diminish test code coverage and quality.
While not difficult, this takes more time, work and
skill&mdash;factors that tend to diminish test code
coverage and quality.

View File

@ -32,8 +32,15 @@ You can run the <live-example></live-example> in Plunker and download the code f
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
the form-specific directives and techniques described in this page.
~~~ {.l-sub-section}
You can also use a reactive (or model-driven) approach to build forms.
However, this page focuses on template-driven forms.
~~~
You can build almost any form with an Angular template&mdash;login forms, contact forms, and pretty much any business form.
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
@ -60,7 +67,14 @@ If you delete the hero name, the form displays a validation error in an attentio
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
~~~ {.l-sub-section}
You can customize the colors and location of the "required" bar with standard CSS.
~~~
You'll build this form in small steps:
1. Create the `Hero` model class.
@ -89,7 +103,9 @@ and one optional field (`alterEgo`).
In the `!{_appDir}` directory, create the following file with the given content:
{@example 'forms/ts/src/app/hero.ts'}
<code-example path="forms/src/app/hero.ts">
</code-example>
It's an anemic model with few requirements and no behavior. Perfect for the demo.
@ -100,6 +116,12 @@ 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.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
</code-example>
## Create a form component
An Angular form has two parts: an HTML-based _template_ and a component _class_
@ -107,6 +129,12 @@ to handle data and user interactions programmatically.
Begin with the class because it states, in brief, what the hero editor can do.
Create the following file with the given content:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/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.
@ -114,7 +142,6 @@ Understanding this component requires only the Angular concepts covered in previ
- The code imports the Angular core library and the `Hero` model you just created.
- The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
- The `moduleId: module.id` property sets the base for module-relative loading of the `templateUrl`.
- The `templateUrl` property points to a separate file for the template HTML.
- You defined dummy data for `model` and `powers`, as befits a demo.
Down the road, you can inject a data service to get and save real data
@ -148,8 +175,13 @@ Because template-driven forms are in their own module, you need to add the `Form
Replace the contents of the "QuickStart" version with the following:
{@example 'forms/ts/src/app/app.module.ts'}
<code-example path="forms/src/app/app.module.ts">
</code-example>
~~~ {.l-sub-section}
There are three changes:
@ -162,6 +194,10 @@ access to all of the template-driven forms features, including `ngModel`.
the `HeroFormComponent` component visible throughout this module.
~~~
~~~ {.alert.is-important}
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
@ -178,20 +214,31 @@ If you wrote it and it should belong to this module, _do_ declare it in th
Replace the contents of the "QuickStart" version with the following:
{@example 'forms/ts/src/app/app.component.ts'}
<code-example path="forms/src/app/app.component.ts">
</code-example>
~~~ {.l-sub-section}
There are only two changes.
The `template` is simply the new element tag identified by the component's `selector` property.
This displays the hero form when the application component is loaded.
You've also dropped the `name` field from the class body.
~~~
## Create an initial HTML form template
Create the template file with the following contents:
{@example 'forms/ts/src/app/hero-form.component.html' region='start'}
<code-example path="forms/src/app/hero-form.component.html" region="start">
</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.
@ -223,6 +270,12 @@ the styles of any external library. Angular apps can use any CSS library or none
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
</code-example>
## Add powers with _*ngFor_
The hero must choose one superpower from a fixed list of agency-approved powers.
@ -233,6 +286,12 @@ form and bind the options to the `powers` list using `ngFor`,
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.component.html" linenums="false" title="src/app/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;
you display its name using the interpolation syntax.
@ -259,9 +318,22 @@ 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.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example>
~~~ {.l-sub-section}
You added a diagnostic interpolation after the input tag
so you can see what you're doing.
You left yourself a note to throw it away when you're done.
~~~
Focus on the binding syntax: `[(ngModel)]="..."`.
If you ran the app now and started typing in the *Name* input box,
@ -276,18 +348,32 @@ At some point it might look like this:
The diagnostic is evidence that values really are flowing from the input box to the model and
back again.
~~~ {.l-sub-section}
That's *two-way data binding*.
For more information, see
[Two-way binding with NgModel](guide/template-syntax) on the
the [Template Syntax](guide/template-syntax) page.
~~~
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
~~~ {.l-sub-section}
Internally, Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
Read more in [The NgForm directive](guide/forms#ngForm), later in this page.
~~~
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
You'll ditch the input box binding message
and add a new binding (at the top) to the component's `diagnostic` property.
@ -295,9 +381,22 @@ 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.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example>
~~~ {.l-sub-section}
- Each input element has an `id` property that is used by the `label` element's `for` attribute
to match the label to its input control.
- Each input element has a `name` property that is required by Angular forms to register the control with the form.
~~~
If you run the app now and change every hero model property, the form might display like this:
<figure class='image-display'>
@ -403,6 +502,12 @@ You can leverage those class names to change the appearance of the control.
Temporarily add a [template reference variable](guide/template-syntax) 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.component.html" linenums="false" title="src/app/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*:
@ -442,9 +547,17 @@ You achieve this effect by adding these class definitions to a new `forms.css` f
that you add to the project as a sibling to `index.html`:
{@example 'forms/ts/src/forms.css'}
<code-example path="forms/src/forms.css">
</code-example>
Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
</code-example>
## Show and hide validation error messages
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
@ -462,15 +575,34 @@ To achieve this effect, extend the `<input>` tag with the following:
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/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".
~~~ {.l-sub-section}
Why "ngModel"?
A directive's [exportAs](api/core/index/Directive-decorator) property
tells Angular how to link the reference variable to the directive.
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "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.component.html" linenums="false" title="src/app/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.
@ -494,11 +626,15 @@ 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.
{@example 'forms/ts/src/app/hero-form.component.html' region='new-hero-button-no-reset'}
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset">
</code-example>
{@example 'forms/ts/src/app/hero-form.component.ts' region='new-hero'}
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false">
</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.
@ -518,7 +654,9 @@ 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.
{@example 'forms/ts/src/app/hero-form.component.html' region='new-hero-button-form-reset'}
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset">
</code-example>
Now clicking "New Hero" resets both the form and its control flags.
@ -532,11 +670,20 @@ trigger a form submit because of its type (`type="submit"`).
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.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example>
You added something extra at the end. You defined a
template reference variable, `#heroForm`, and initialized it with the value "ngForm".
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
~~~ {.l-sub-section}
### The _NgForm_ directive
What `NgForm` directive?
@ -549,9 +696,19 @@ It holds the controls you created for the elements with an `ngModel` directive
and `name` attribute, and monitors their properties, including their validity.
It also has its own `valid` property which is true only *if every contained
control* is valid.
~~~
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.component.html" linenums="false" title="src/app/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.
@ -571,24 +728,49 @@ For you, it was as simple as this:
Submitting the form isn't terribly dramatic at the moment.
~~~ {.l-sub-section}
An unsurprising observation for a demo. To be honest,
jazzing it up won't teach you anything new about forms.
But this is an opportunity to exercise some of your newly won
binding skills.
If you aren't interested, skip to this page's conclusion.
~~~
For a more strikingly visual effect,
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.component.html" linenums="false" title="src/app/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.component.ts" linenums="false" title="src/app/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.
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.component.html" linenums="false" title="src/app/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.
@ -685,47 +867,48 @@ The final project folder structure should look like this:
Heres the code for the final version of the application:
<md-tab-group>
<md-tab label="hero-form.component.ts">
{@example 'forms/ts/src/app/hero-form.component.ts' region='final'}
</md-tab>
<code-tabs>
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
</code-pane>
<md-tab label="hero-form.component.html">
{@example 'forms/ts/src/app/hero-form.component.html' region='final'}
</md-tab>
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
</code-pane>
<md-tab label="hero.ts">
{@example 'forms/ts/src/app/hero.ts'}
</md-tab>
<code-pane title="hero.ts" path="forms/src/app/hero.ts">
</code-pane>
<md-tab label="app.module.ts">
{@example 'forms/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="app.module.ts" path="forms/src/app/app.module.ts">
</code-pane>
<md-tab label="app.component.ts">
{@example 'forms/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
</code-pane>
<md-tab label="main.ts">
{@example 'forms/ts/src/main.ts'}
</md-tab>
<code-pane title="main.ts" path="forms/src/main.ts">
</code-pane>
<md-tab label="index.html">
{@example 'forms/ts/src/index.html'}
</md-tab>
<code-pane title="index.html" path="forms/src/index.html">
</code-pane>
<md-tab label="forms.css">
{@example 'forms/ts/src/forms.css'}
</md-tab>
<code-pane title="forms.css" path="forms/src/forms.css">
</code-pane>
</md-tab-group>
</code-tabs>

View File

@ -4,3 +4,5 @@ Glossary
@intro
Brief definitions of the most important words in the Angular vocabulary.
@description

View File

@ -5,6 +5,7 @@ Hierarchical Dependency Injectors
Angular's hierarchical dependency injection system supports nested injectors in parallel with the component tree.
@description
You learned the basics of Angular Dependency injection in the
[Dependency Injection](guide/dependency-injection) guide.
@ -26,10 +27,17 @@ An application may have multiple injectors.
An Angular application is a tree of components. Each component instance has its own injector.
The tree of components parallels the tree of injectors.
~~~ {.l-sub-section}
The component's injector may be a _proxy_ for an ancestor injector higher in the component tree.
That's an implementation detail that improves efficiency.
You won't notice the difference and
your mental model should be that every component has its own injector.
~~~
Consider this guide's variation on the Tour of Heroes application.
At the top is the `AppComponent` which has some sub-components.
One of them is the `HeroesListComponent`.
@ -49,9 +57,16 @@ If that injector can't satisfy the request, it passes it along to *its* parent i
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.
~~~ {.l-sub-section}
You can cap the bubbling. An intermediate component can declare that it is the "host" component.
The hunt for providers will climb no higher than the injector for that host component.
This is a topic for another day.
~~~
### Re-providing a service at different levels
You can re-register a provider for a particular dependency token at multiple levels of the injector tree.
@ -83,6 +98,12 @@ If you later modified the `VillainsService`, you could break something in a hero
That's not supposed to happen but providing the service in the root `AppModule` creates that risk.
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="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.
It's still a singleton, but it's a singleton that exist solely in the _villain_ domain.
@ -102,6 +123,7 @@ To open a hero's tax return, the preparer clicks on a hero name, which opens a c
Each selected hero tax return opens in its own component and multiple returns can be open at the same time.
Each tax return component has the following characteristics:
* Is its own tax return editing session.
* Can change a tax return without affecting a return in another component.
* Has the ability to save the changes to its tax return or cancel them.
@ -120,12 +142,16 @@ It caches a single `HeroTaxReturn`, tracks changes to that return, and can save
It also delegates to the application-wide singleton `HeroService`, which it gets by injection.
{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts'}
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts">
</code-example>
Here is the `HeroTaxReturnComponent` that makes use of it.
{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts'}
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts">
</code-example>
The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters.
The setter initializes the component's own instance of the `HeroTaxReturnService` with the incoming return.
@ -138,13 +164,26 @@ Each component would overwrite the tax return that belonged to another hero.
What a mess!
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="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.
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service.
No tax return overwriting. No mess.
~~~ {.l-sub-section}
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
You can review it and download it from the <live-example></live-example>.
~~~
### Scenario: specialized providers
Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service,
@ -176,5 +215,11 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
</figure>
~~~ {.l-sub-section}
The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample
which you can review and download from the <live-example></live-example>.
which you can review and download from the <live-example></live-example>.
~~~

View File

@ -40,8 +40,15 @@ This page describes the _i18n_ tools available to assist translation of componen
into multiple languages.
~~~ {.l-sub-section}
Practitioners of _internationalization_ refer to a translatable text as a "_message_".
This page uses the words "_text_" and "_message_" interchangably and in the combination, "_text message_".
~~~
The _i18n_ template translation process has four phases:
1. Mark static text messages in your component templates for translation.
@ -78,12 +85,16 @@ After translation, the compiler removes it.
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish:
{@example 'cb-i18n/ts/src/app/app.component.1.html' region='greeting'}
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false">
</code-example>
Add the `i18n` attribute to the tag to mark it for translation.
{@example 'cb-i18n/ts/src/app/app.component.1.html' region='i18n-attribute'}
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false">
</code-example>
### Help the translator with a _description_ and _intent_
@ -92,7 +103,9 @@ need a description of the message.
Assign a description to the i18n attribute:
{@example 'cb-i18n/ts/src/app/app.component.1.html' region='i18n-attribute-desc'}
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false">
</code-example>
In order to deliver a correct translation, the translator may need to
know your _intent_&mdash;the true _meaning_ of the text
@ -101,7 +114,9 @@ In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`):
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-attribute-meaning'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false">
</code-example>
While all appearances of a message with the _same_ meaning have the _same_ translation,
a message with *a variety of possible meanings* could have different translations.
@ -119,12 +134,16 @@ Here are two techniques to try.
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-ng-container'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false">
</code-example>
(2) Wrap the text in a pair of HTML comments:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-with-comment'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false">
</code-example>
@ -134,7 +153,9 @@ Here are two techniques to try.
You've added an image to your template. You care about accessibility too so you add a `title` attribute:
{@example 'cb-i18n/ts/src/app/app.component.1.html' region='i18n-title'}
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false">
</code-example>
The `title` attribute needs to be translated.
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
@ -143,7 +164,9 @@ name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-title-translate'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false">
</code-example>
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
@ -161,7 +184,9 @@ Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-plural'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false">
</code-example>
* The first parameter is the key. It is bound to the component property (`wolves`)
that determines the number of wolves.
@ -185,6 +210,9 @@ You could keep this up for three, four, and every other number of wolves.
Or you could specify the **`other`** category as a catch-all for any unmatched cardinality
and write something like: `other {a wolf pack}`.
~~~ {.l-sub-section}
This syntax conforms to the
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU Message Format</a>
that derives from the
@ -193,6 +221,10 @@ which specifies the
<a href="http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules" target="_blank" title="Pluralization Rules">pluralization rules</a>.
~~~
{@a select}
## Select among alternative texts
The application displays different text depending upon whether the hero is male or female.
@ -208,7 +240,9 @@ property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-select'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example>
@ -235,7 +269,14 @@ Open a terminal window at the root of the application project and enter the `ng-
</code-example>
~~~ {.l-sub-section}
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
~~~
By default, the tool generates a translation file named **`messages.xlf`** in the
<a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">XML Localisation Interchange File Format (XLIFF, version 1.2)</a>.
@ -312,8 +353,15 @@ for the project structure to reflect your entire internationalization effort.
One approach is to dedicate a folder to localization and store related assets
(for example, internationalization files) there.
~~~ {.l-sub-section}
Localization and internationalization are
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank">different but closely related terms</a>.This cookbook follows that suggestion. It has a `locale` folder under the `src/`.
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank">different but closely related terms</a>.
~~~
This cookbook follows that suggestion. It has a `locale` folder under the `src/`.
Assets within the folder carry a filename extension that matches a language-culture code from a
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx" target="_blank">well-known codeset</a>.
@ -330,14 +378,18 @@ This sample file is easy to translate without a special editor or knowledge of S
Open `messages.es.xlf` and find the first `<trans-unit>` section:
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translated-hello'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example>
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting:
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translated-hello'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example>
@ -354,7 +406,9 @@ See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
Translate the other text nodes the same way:
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translated-other-nodes'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false">
</code-example>
@ -373,13 +427,17 @@ In this example, you know the translation unit for the `select` must be just bel
To translate a `plural`, translate its ICU format match values:
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translated-plural'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false">
</code-example>
### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template:
{@example 'cb-i18n/ts/src/app/app.component.html' region='i18n-select'}
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example>
The extraction tool broke that into _two_ translation units.
@ -388,17 +446,23 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and leave the placeholder where it is.
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translate-select-1'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false">
</code-example>
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translate-select-2'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false">
</code-example>
Here they are together, after translation:
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html' region='translated-select'}
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false">
</code-example>
<div class='l-main-content'>
@ -416,34 +480,35 @@ time to incorporate that translation into the application.
When the previous steps finish, the sample app _and_ its translation file are as follows:
<md-tab-group>
<md-tab label="src/app/app.component.html">
{@example 'cb-i18n/ts/src/app/app.component.html'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html">
</code-pane>
<md-tab label="src/app/app.component.ts">
{@example 'cb-i18n/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts">
</code-pane>
<md-tab label="src/app/app.module.ts">
{@example 'cb-i18n/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts">
</code-pane>
<md-tab label="src/main.ts">
{@example 'cb-i18n/ts/src/main.1.ts'}
</md-tab>
<code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts">
</code-pane>
<md-tab label="src/locale/messages.es.xlf">
{@example 'cb-i18n/ts/src/locale/messages.es.xlf.html'}
</md-tab>
<code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html">
</code-pane>
</md-tab-group>
</code-tabs>
@ -483,7 +548,9 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows:
{@example 'cb-i18n/ts/src/index.html' region='i18n'}
<code-example path="cb-i18n/src/index.html" region="i18n" linenums="false">
</code-example>
In this sample, the user's language is hardcoded as a global `document.locale` variable
in the `index.html`.
@ -500,7 +567,9 @@ You'll need it to import the language translation file.
SystemJS doesn't ship with a raw text plugin but it's easy to add.
Create the following `systemjs-text-plugin.js` in the `src/` folder:
{@example 'cb-i18n/ts/src/systemjs-text-plugin.js'}
<code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false">
</code-example>
### Create translation providers
@ -515,7 +584,9 @@ The `getTranslationProviders` function in the following `src/app/i18n-providers.
creates those providers based on the user's _locale_
and the corresponding translation file:
{@example 'cb-i18n/ts/src/app/i18n-providers.ts'}
<code-example path="cb-i18n/src/app/i18n-providers.ts">
</code-example>
1. It gets the locale from the global `document.locale` variable that was set in `index.html`.
@ -542,7 +613,9 @@ You'll create an _options_ object with the translation providers from `getTransl
and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows:
{@example 'cb-i18n/ts/src/main.ts'}
<code-example path="cb-i18n/src/main.ts" linenums="false">
</code-example>
Notice that it waits for the `getTranslationProviders` promise to resolve before
bootstrapping the app.
@ -592,6 +665,9 @@ For this sample, the Spanish language command would be
</code-example>
~~~ {.l-sub-section}
Windows users may have to quote the command:
<code-example language="sh" class="code-shell">
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
@ -600,6 +676,10 @@ Windows users may have to quote the command:
~~~
{@a maintenance}
## Translation file maintenance and _id_ changes

View File

@ -5,6 +5,7 @@ Documentation Overview
How to read and use this documentation.
@description
This page describes the Angular documentation at a high level.
If you're new to Angular, you may want to visit "[Learning Angular](guide/learning-angular)" first.
@ -13,6 +14,7 @@ If you're new to Angular, you may want to visit "[Learning Angular](guide/learni
The documentation is divided into major thematic sections, each
a collection of pages devoted to that theme.
<table width="100%">
<col width="15%">

View File

@ -6,6 +6,7 @@ A suggested path through the documentation for Angular newcomers.
@description
<figure>
<img src="assets/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"> </img>
</figure>
@ -40,7 +41,13 @@ from small, single-purpose parts.
After reading the above sections, feel free to skip around among the other pages on this site.
~~~ {.l-sub-section}
### Next Step
Try the [tutorial](tutorial "Tour of Heroes") if you're ready to start coding or
visit the [Architecture](guide/architecture) page if you prefer to learn the basic concepts first.
visit the [Architecture](guide/architecture) page if you prefer to learn the basic concepts first.
~~~

View File

@ -6,11 +6,12 @@ Angular calls lifecycle hook methods on directives and components as it creates,
@description
<figure>
<img src="assets/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"> </img>
</figure>
A component has a lifecycle managed by Angular itself.
A component has a lifecycle managed by Angular.
Angular creates it, renders it, creates and renders its children,
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
@ -24,17 +25,19 @@ A directive has the same set of lifecycle hooks, minus the hooks that are specif
{@a hooks-overview}
## Component lifecycle hooks
## Component lifecycle hooks overview
Directive and component instances have a lifecycle
as Angular creates, updates, and destroys them.
Developers can tap into key moments in that lifecycle by implementing
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
one or more of the *lifecycle hook* interfaces in the Angular `core` library.
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
For example, the `OnInit` interface has a hook method named `ngOnInit`
For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component:
{@example 'lifecycle-hooks/ts/src/app/peek-a-boo.component.ts' region='ngOnInit'}
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false">
</code-example>
No directive or component will implement all of the lifecycle hooks and some of the hooks only make sense for components.
Angular only calls a directive/component hook method *if it is defined*.
@ -75,7 +78,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngOnChanges
<code>ngOnChanges()</code>
</td>
@ -83,7 +86,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values.
Called before `ngOnInit` and whenever one or more data-bound input properties change.
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
</td>
@ -94,7 +97,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngOnInit
<code>ngOnInit()</code>
</td>
@ -102,7 +105,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties.
Called _once_, after the _first_ `ngOnChanges`.
Called _once_, after the _first_ `ngOnChanges()`.
</td>
@ -113,14 +116,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngDoCheck
<code>ngDoCheck()</code>
</td>
<td>
Detect and act upon changes that Angular can't or won't detect on its own.
Called during every change detection run, immediately after `ngOnChanges` and `ngOnInit`.
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
</td>
@ -131,14 +134,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngAfterContentInit
<code>ngAfterContentInit()</code>
</td>
<td>
Respond after Angular projects external content into the component's view.
Called _once_ after the first `NgDoCheck`.
Called _once_ after the first `ngDoCheck()`.
_A component-only hook_.
@ -151,14 +154,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngAfterContentChecked
<code>ngAfterContentChecked()</code>
</td>
<td>
Respond after Angular checks the content projected into the component.
Called after the `ngAfterContentInit` and every subsequent `NgDoCheck`.
Called after the `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
_A component-only hook_.
@ -171,14 +174,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngAfterViewInit
<code>ngAfterViewInit()</code>
</td>
<td>
Respond after Angular initializes the component's views and child views.
Called _once_ after the first `ngAfterContentChecked`.
Called _once_ after the first `ngAfterContentChecked()`.
_A component-only hook_.
@ -191,14 +194,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngAfterViewChecked
<code>ngAfterViewChecked()</code>
</td>
<td>
Respond after Angular checks the component's views and child views.
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked`.
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked()`.
_A component-only hook_.
@ -211,13 +214,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<tr style=top>
<td>
ngOnDestroy
<code>ngOnDestroy</code>
</td>
<td>
Cleanup just before Angular destroys the directive/component.
Unsubscribe observables and detach event handlers to avoid memory leaks.
Unsubscribe Observables and detach event handlers to avoid memory leaks.
Called _just before_ Angular destroys the directive/component.
@ -233,19 +236,20 @@ calls the lifecycle hook methods in the following sequence at specific moments:
{@a other-lifecycle-hooks}
## Other lifecycle hooks
## Other Angular lifecycle hooks
Other Angular sub-systems may have their own lifecycle hooks apart from these component hooks.
3rd party libraries might implement their hooks as well in order to give developers more
control over how these libraries are used.
## Lifecycle exercises
## Lifecycle examples
The <live-example></live-example>
demonstrates the lifecycle hooks in action through a series of exercises
presented as components under the control of the root `AppComponent`.
They follow a common pattern: a *parent* component serves as a test rig for
They follow a common pattern: a *parent* component serves as a test rig for
a *child* component that illustrates one or more of the lifecycle hook methods.
Here's a brief description of each exercise:
@ -321,7 +325,7 @@ Here's a brief description of each exercise:
<td>
See how Angular calls the `ngOnChanges` hook with a `changes` object
See how Angular calls the `ngOnChanges()` hook with a `changes` object
every time one of the component input properties changes.
Shows how to interpret the `changes` object.
</td>
@ -338,7 +342,7 @@ Here's a brief description of each exercise:
<td>
Implements an `ngDoCheck` method with custom change detection.
Implements an `ngDoCheck()` method with custom change detection.
See how often Angular calls this hook and watch it post changes to a log.
</td>
@ -403,7 +407,7 @@ Here's a brief description of each exercise:
</table>
The remainder of this chapter discusses selected exercises in further detail.
The remainder of this page discusses selected exercises in further detail.
{@a peek-a-boo}
@ -423,10 +427,17 @@ The sequence of log messages follows the prescribed hook calling order:
`OnChanges`, `OnInit`, `DoCheck`&nbsp;(3x), `AfterContentInit`, `AfterContentChecked`&nbsp;(3x),
`AfterViewInit`, `AfterViewChecked`&nbsp;(3x), and `OnDestroy`.
~~~ {.l-sub-section}
The constructor isn't an Angular hook *per se*.
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.
~~~
Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
Clearly these three hooks fire a *often*. Keep the logic in these hooks as lean as possible!
Clearly these three hooks fire *often*. Keep the logic in these hooks as lean as possible!
The next examples focus on hook details.
@ -440,26 +451,37 @@ Go undercover with these two spy hooks to discover when an element is initialize
This is the perfect infiltration job for a directive.
The heroes will never know they're being watched.
~~~ {.l-sub-section}
Kidding aside, pay attention to two key points:
1. Angular calls hook methods for *directives* as well as components.<br><br>
2. A spy directive can provide insight into a DOM object that you cannot change directly.
Obviously you can't touch the implementation of a native `div`.
Obviously you can't touch the implementation of a native `<div>`.
You can't modify a third party component either.
But you can watch both with a directive.
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
~~~
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
that log messages to the parent via an injected `LoggerService`.
{@example 'lifecycle-hooks/ts/src/app/spy.directive.ts' region='spy-directive'}
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false">
</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>`
Here it is attached to the repeated hero `<div>`:
{@example 'lifecycle-hooks/ts/src/app/spy.component.html' region='template'}
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false">
</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:
@ -468,72 +490,98 @@ with an entry in the *Hook Log* as seen here:
<img src='assets/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"> </img>
</figure>
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit()` logs that event.
The *Reset* button clears the `heroes` list.
Angular removes all hero `<div>` elements from the DOM and destroys their spy directives at the same time.
The spy's `ngOnDestroy` method reports its last moments.
The spy's `ngOnDestroy()` method reports its last moments.
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
The `ngOnInit()` and `ngOnDestroy()` methods have more vital roles to play in real applications.
### OnInit
{@a oninit}
### _OnInit()_
Use `ngOnInit` for two main reasons:
1. to perform complex initializations shortly after construction
1. to set up the component after Angular sets the input properties
Use `ngOnInit()` for two main reasons:
1. To perform complex initializations shortly after construction.
1. To set up the component after Angular sets the input properties.
Experienced developers agree that components should be cheap and safe to construct.
~~~ {.l-sub-section}
Misko Hevery, Angular team lead,
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
you should avoid complex constructor logic.
~~~
Don't fetch data in a component constructor.
You shouldn't worry that a new component will try to contact a remote server when
created under test or before you decide to display it.
Constructors should do no more than set the initial local variables to simple values.
An `ngOnInit` is a good place for a component to fetch its initial data. The
[Tutorial](tutorial/toh-pt4) and [HTTP](guide/server-communication) chapter
show how.
An `ngOnInit()` is a good place for a component to fetch its initial data. The
[Tour of Heroes Tutorial](tutorial/toh-pt4) and [HTTP Client](guide/server-communication)
guides show how.
Remember also that a directive's data-bound input properties are not set until _after construction_.
That's a problem if you need to initialize the directive based on those properties.
They'll have been set when `ngOninit` runs.
The `ngOnChanges` method is your first opportunity to access those properties.
Angular calls `ngOnChanges` before `ngOnInit` ... and many times after that.
It only calls `ngOnInit` once.You can count on Angular to call the `ngOnInit` method _soon_ after creating the component.
They'll have been set when `ngOnInit()` runs.
~~~ {.l-sub-section}
The `ngOnChanges()` method is your first opportunity to access those properties.
Angular calls `ngOnChanges()` before `ngOnInit()` and many times after that.
It only calls `ngOnInit()` once.
~~~
You can count on Angular to call the `ngOnInit()` method _soon_ after creating the component.
That's where the heavy initialization logic belongs.
### OnDestroy
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
{@a ondestroy}
### _OnDestroy()_
Put cleanup logic in `ngOnDestroy()`, the logic that *must* run before Angular destroys the directive.
This is the time to notify another part of the application that the component is going away.
This is the place to free resources that won't be garbage collected automatically.
Unsubscribe from observables and DOM events. Stop interval timers.
Unsubscribe from Observables and DOM events. Stop interval timers.
Unregister all callbacks that this directive registered with global or application services.
You risk memory leaks if you neglect to do so.
## OnChanges
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
{@a onchanges}
## _OnChanges()_
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook.
{@example 'lifecycle-hooks/ts/src/app/on-changes.component.ts' region='ng-on-changes'}
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false">
The `ngOnChanges` method takes an object that maps each changed property name to a
</code-example>
The `ngOnChanges()` method takes an object that maps each changed property name to a
[SimpleChange](api/core/index/SimpleChange-class) object holding the current and previous property values.
This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
{@example 'lifecycle-hooks/ts/src/app/on-changes.component.ts' region='inputs'}
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false">
</code-example>
The host `OnChangesParentComponent` binds to them like this:
{@example 'lifecycle-hooks/ts/src/app/on-changes-parent.component.html' region='on-changes'}
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes">
</code-example>
Here's the sample in action as the user makes changes.
@ -550,13 +598,25 @@ The value of the `hero` property is the *reference to the hero object*.
Angular doesn't care that the hero's own `name` property changed.
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
## DoCheck
{@a docheck}
## _DoCheck()_
Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
Use this method to detect a change that Angular overlooked.The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck` hook:
{@example 'lifecycle-hooks/ts/src/app/do-check.component.ts' region='ng-do-check'}
~~~ {.l-sub-section}
This code inspects certain _values-of-interest_, capturing and comparing their current state against previous values.
Use this method to detect a change that Angular overlooked.
~~~
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" linenums="false">
</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`
so you can see how often `DoCheck` is called. The results are illuminating:
@ -564,73 +624,99 @@ so you can see how often `DoCheck` is called. The results are illuminating:
<img src='assets/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"> </img>
</figure>
While the `ngDoCheck` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency &mdash;
after _every_ change detection cycle no matter where the change occurred.
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency&mdash;after _every_
change detection cycle no matter where the change occurred.
It's called over twenty times in this example before the user can do anything.
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
Mere mousing into another input box triggers a call.
Mere mousing into another `<input>` triggers a call.
Relatively few calls reveal actual changes to pertinent data.
Clearly our implementation must be very lightweight or the user experience will suffer.
Clearly our implementation must be very lightweight or the user experience suffers.
{@a afterview}
## AfterView
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` hooks that Angular calls
*after* it creates a component's child views.
Here's a child view that displays a hero's name in an input box:
Here's a child view that displays a hero's name in an `<input>`:
{@example 'lifecycle-hooks/ts/src/app/after-view.component.ts' region='child-view'}
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false">
</code-example>
The `AfterViewComponent` displays this child view *within its template*:
{@example 'lifecycle-hooks/ts/src/app/after-view.component.ts' region='template'}
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false">
The following hooks take action based on changing values *within the child view*
</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/index/ViewChild-decorator).
{@example 'lifecycle-hooks/ts/src/app/after-view.component.ts' region='hooks'}
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false">
</code-example>
<div id='wait-a-tick'>
</div>
{@a wait-a-tick}
### Abide by the unidirectional data flow rule
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
{@example 'lifecycle-hooks/ts/src/app/after-view.component.ts' region='do-something'}
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false">
Why does the `doSomething` method wait a tick before updating `comment`?
</code-example>
Why does the `doSomething()` method wait a tick before updating `comment`?
Angular's unidirectional data flow rule forbids updates to the view *after* it has been composed.
Both of these hooks fire _after_ the component's view has been composed.
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).Here's *AfterView* in action
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).
The `LoggerService.tick_then()` postpones the log update
for one turn of the browser's JavaScript cycle and that's just long enough.
Here's *AfterView* in action:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"> </img>
</figure>
Notice that Angular frequently calls `AfterViewChecked`, often when there are no changes of interest.
Notice that Angular frequently calls `AfterViewChecked()`, often when there are no changes of interest.
Write lean hook methods to avoid performance problems.
{@a aftercontent}
## AfterContent
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls
*after* Angular projects external content into the component.
{@a content-projection}
### Content projection
*Content projection* is a way to import HTML content from outside the component and insert that content
into the component's template in a designated spot.
~~~ {.l-sub-section}
AngularJS developers know this technique as *transclusion*.
~~~
Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afterview) example.
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.
the `AfterContentComponent`'s parent. Here's the parent's template:
{@example 'lifecycle-hooks/ts/src/app/after-content.component.ts' region='parent-template'}
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false">
</code-example>
Notice that the `<my-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
@ -638,7 +724,9 @@ into the component*.
Now look at the component's template:
{@example 'lifecycle-hooks/ts/src/app/after-content.component.ts' region='template'}
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false">
</code-example>
The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
@ -648,10 +736,21 @@ In this case, the projected content is the `<my-child>` from the parent.
</figure>
The tell-tale signs of *content projection* are (a) HTML between component element tags
and (b) the presence of `<ng-content>` tags in the component's template.### AfterContent hooks
~~~ {.l-sub-section}
The telltale signs of *content projection* are twofold:
- HTML between component element tags.
- The presence of `<ng-content>` tags in the component's template.
~~~
{@a aftercontent-hooks}
### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks.
The key difference is in the child component
The key difference is in the child component.
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
appear *within* the component's template.
@ -659,19 +758,21 @@ appear *within* the component's template.
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
projected into the component.
The following *AfterContent* hooks take action based on changing values in a *content child*
which can only be reached by querying for it via the property decorated with
The following *AfterContent* hooks take action based on changing values in a *content child*,
which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/index/ContentChild-decorator).
{@example 'lifecycle-hooks/ts/src/app/after-content.component.ts' region='hooks'}
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false">
</code-example>
{@a no-unidirectional-flow-worries}
### No unidirectional flow worries with _AfterContent..._
### No unidirectional flow worries with _AfterContent_
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
This component's `doSomething()` method update's the component's data-bound `comment` property immediately.
There's no [need to wait](guide/lifecycle-hooks#wait-a-tick).
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.

View File

@ -1,5 +1,6 @@
@description
<style>
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ NgModules
Define application modules with @NgModule.
@description
**NgModules** help organize an application into cohesive blocks of functionality.
<!-- CF: "app" and "application" are used interchangeably throughout this page.
I'm not sure what's appropriate, so I left them as is for now. -->
@ -121,7 +122,9 @@ By convention, the *root module* class is called `AppModule` and it exists in a
The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible:
{@example 'setup/ts/src/app/app.module.ts'}
<code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example>
The `@NgModule` decorator defines the metadata for the module.
This page takes an intuitive approach to understanding the metadata and fills in details as it progresses.
@ -137,7 +140,9 @@ the _root component_, the top of the app's rather bare component tree.
The example `AppComponent` simply displays a data-bound title:
{@example 'ngmodule/ts/src/app/app.component.0.ts'}
<code-example path="ngmodule/src/app/app.component.0.ts" linenums="false">
</code-example>
Lastly, the `@NgModule.bootstrap` property identifies this `AppComponent` as the _bootstrap component_.
When Angular launches the app, it places the HTML rendering of `AppComponent` in the DOM,
@ -157,7 +162,9 @@ In the first, _dynamic_ option, the [Angular compiler](cookbook/ngmodule-faq)
compiles the application in the browser and then launches the app.
{@example 'ngmodule/ts/src/main.ts'}
<code-example path="ngmodule/src/main.ts" linenums="false">
</code-example>
The samples in this page demonstrate the dynamic bootstrapping approach.
@ -177,7 +184,9 @@ The syntax for bootstrapping the pre-compiled `AppModuleNgFactory` is similar to
the dynamic version that bootstraps the `AppModule` class.
{@example 'ngmodule/ts/src/main-static.ts'}
<code-example path="ngmodule/src/main-static.ts" linenums="false">
</code-example>
Because the entire application was pre-compiled,
Angular doesn't ship the Angular compiler to the browser and doesn't compile in the browser.
@ -209,39 +218,53 @@ As the app evolves,
the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives)
that sets the background color of the attached element.
{@example 'ngmodule/ts/src/app/highlight.directive.ts'}
<code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false">
</code-example>
Update the `AppComponent` template to attach the directive to the title:
{@example 'ngmodule/ts/src/app/app.component.1.ts' region='template'}
<code-example path="ngmodule/src/app/app.component.1.ts" region="template" linenums="false">
</code-example>
If you ran the app now, Angular wouldn't recognize the `highlight` attribute and would ignore it.
You must declare the directive in `AppModule`.
Import the `HighlightDirective` class and add it to the module's `declarations` like this:
{@example 'ngmodule/ts/src/app/app.module.1.ts' region='directive'}
<code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false">
</code-example>
### Add a component
Refactor the title into its own `TitleComponent`.
The component's template binds to the component's `title` and `subtitle` properties like this:
{@example 'ngmodule/ts/src/app/title.component.html' region='v1'}
<code-example path="ngmodule/src/app/title.component.html" region="v1" linenums="false">
</code-example>
{@example 'ngmodule/ts/src/app/title.component.ts' region='v1'}
<code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false">
</code-example>
Rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element,
using an input binding to set the `subtitle`.
{@example 'ngmodule/ts/src/app/app.component.1.ts'}
<code-example path="ngmodule/src/app/app.component.1.ts" linenums="false">
</code-example>
Angular won't recognize the `<app-title>` tag until you declare it in `AppModule`.
Import the `TitleComponent` class and add it to the module's `declarations`:
{@example 'ngmodule/ts/src/app/app.module.1.ts' region='component'}
<code-example path="ngmodule/src/app/app.module.1.ts" region="component" linenums="false">
</code-example>
@ -264,22 +287,30 @@ accessible through a user service.
This sample application has a dummy implementation of such a `UserService`.
{@example 'ngmodule/ts/src/app/user.service.ts'}
<code-example path="ngmodule/src/app/user.service.ts" linenums="false">
</code-example>
The sample application should display a welcome message to the logged-in user just below the application title.
Update the `TitleComponent` template to show the welcome message below the application title.
{@example 'ngmodule/ts/src/app/title.component.html'}
<code-example path="ngmodule/src/app/title.component.html" linenums="false">
</code-example>
Update the `TitleComponent` class with a constructor that injects the `UserService`
and sets the component's `user` property from the service.
{@example 'ngmodule/ts/src/app/title.component.ts'}
<code-example path="ngmodule/src/app/title.component.ts" linenums="false">
</code-example>
You've defined and used the service. Now to _provide_ it for all components to use,
add it to a `providers` property in the `AppModule` metadata:
{@example 'ngmodule/ts/src/app/app.module.1.ts' region='providers'}
<code-example path="ngmodule/src/app/app.module.1.ts" region="providers" linenums="false">
</code-example>
@ -290,7 +321,9 @@ add it to a `providers` property in the `AppModule` metadata:
In the revised `TitleComponent`, an `*ngIf` directive guards the message.
There is no message if there is no user.
{@example 'ngmodule/ts/src/app/title.component.html' region='ngIf'}
<code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false">
</code-example>
Although `AppModule` doesn't declare `NgIf`, the application still compiles and runs.
How can that be? The Angular compiler should either ignore or complain about unrecognized HTML.
@ -298,16 +331,25 @@ How can that be? The Angular compiler should either ignore or complain about unr
Angular does recognize `NgIf` because you imported it earlier.
The initial version of `AppModule` imports `BrowserModule`.
{@example 'ngmodule/ts/src/app/app.module.0.ts' region='imports'}
<code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false">
</code-example>
Importing `BrowserModule` made all of its public components, directives, and pipes visible
to the component templates in `AppModule`.
~~~ {.l-sub-section}
More accurately, `NgIf` is declared in `CommonModule` from `@angular/common`.
`CommonModule` contributes many of the common directives that applications need, including `ngIf` and `ngFor`.
`BrowserModule` imports `CommonModule` and [re-exports](cookbook/ngmodule-faq) it.
The net effect is that an importer of `BrowserModule` gets `CommonModule` directives automatically.Many familiar Angular directives don't belong to `CommonModule`.
The net effect is that an importer of `BrowserModule` gets `CommonModule` directives automatically.
~~~
Many familiar Angular directives don't belong to `CommonModule`.
For example, `NgModel` and `RouterLink` belong to Angular's `FormsModule` and `RouterModule` respectively.
You must import those modules before you can use their directives.
@ -321,6 +363,9 @@ a form component that imports form support from the Angular `FormsModule`.
The `ContactComponent` presents a "contact editor,"
implemented with Angular forms in the [template-driven form](guide/forms) style.
~~~ {.l-sub-section}
### Angular form styles
You can write Angular form components in
@ -332,10 +377,16 @@ The following sample imports the `FormsModule` from `@angular/forms` because
the `ContactComponent` is written in _template-driven_ style.
Modules with components written in the _reactive_ style
import the `ReactiveFormsModule`.
~~~
The `ContactComponent` selector matches an element named `<app-contact>`.
Add an element with that name to the `AppComponent` template, just below the `<app-title>`:
{@example 'ngmodule/ts/src/app/app.component.1b.ts' region='template'}
<code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false">
</code-example>
Form components are often complex. The `ContactComponent` has its own `ContactService`
and [custom pipe](guide/pipes) (called `Awesome`),
@ -343,39 +394,40 @@ and an alternative version of the `HighlightDirective`.
To make it manageable, place all contact-related material in an `src/app/contact` folder
and break the component into three constituent HTML, TypeScript, and css files:
<md-tab-group>
<md-tab label="src/app/contact/contact.component.html">
{@example 'ngmodule/ts/src/app/contact/contact.component.html'}
</md-tab>
<code-tabs>
<code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html">
</code-pane>
<md-tab label="src/app/contact/contact.component.ts">
{@example 'ngmodule/ts/src/app/contact/contact.component.3.ts'}
</md-tab>
<code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts">
</code-pane>
<md-tab label="src/app/contact/contact.component.css">
{@example 'ngmodule/ts/src/app/contact/contact.component.css'}
</md-tab>
<code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css">
</code-pane>
<md-tab label="src/app/contact/contact.service.ts">
{@example 'ngmodule/ts/src/app/contact/contact.service.ts'}
</md-tab>
<code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts">
</code-pane>
<md-tab label="src/app/contact/awesome.pipe.ts">
{@example 'ngmodule/ts/src/app/contact/awesome.pipe.ts'}
</md-tab>
<code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts">
</code-pane>
<md-tab label="src/app/contact/highlight.directive.ts">
{@example 'ngmodule/ts/src/app/contact/highlight.directive.ts'}
</md-tab>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane>
</md-tab-group>
</code-tabs>
In the middle of the component template,
notice the two-way data binding `[(ngModel)]`.
@ -393,7 +445,9 @@ form features such as validation aren't yet available.
Add the `FormsModule` to the `AppModule` metadata's `imports` list.
{@example 'ngmodule/ts/src/app/app.module.1.ts' region='imports'}
<code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false">
</code-example>
Now `[(ngModel)]` binding will work and the user input will be validated by Angular forms,
once you declare the new component, pipe, and directive.
@ -420,21 +474,32 @@ Components, directives, and pipes belong to _one module only_.
The application won't compile until you declare the contact component, directive, and pipe.
Update the `declarations` in the `AppModule` accordingly:
{@example 'ngmodule/ts/src/app/app.module.1.ts' region='declarations'}
<code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false">
</code-example>
{@a import-name-conflict}
~~~ {.l-sub-section}
There are two directives with the same name, both called `HighlightDirective`.
To work around this, create an alias for the contact version using the `as` JavaScript import keyword.
{@example 'ngmodule/ts/src/app/app.module.1b.ts' region='import-alias'}
<code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false">
</code-example>
This solves the immediate issue of referencing both directive _types_ in the same file but
leaves another issue unresolved.
You'll learn more about that issue later in this page, in [Resolve directive conflicts](guide/ngmodule#resolve-conflicts).
~~~
### Provide the _ContactService_
The `ContactComponent` displays contacts retrieved by the `ContactService`,
which Angular injects into its constructor.
@ -446,13 +511,18 @@ You want to share this service with other contact-related components that you'll
In this app, add `ContactService` to the `AppModule` metadata's `providers` list:
{@example 'ngmodule/ts/src/app/app.module.1b.ts' region='providers'}
<code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false">
</code-example>
Now you can inject `ContactService` (like `UserService`) into any component in the application.
{@a application-scoped-providers}
~~~ {.l-sub-section}
### Application-scoped providers
The `ContactService` provider is _application_-scoped because Angular
registers a module's `providers` with the application's *root injector*.
@ -475,6 +545,10 @@ Now you can inject `ContactService` (like `UserService`) into any component in t
Read more in the [How do I restrict service scope to a module?](cookbook/ngmodule-faq) section
of the [NgModule FAQs](cookbook/ngmodule-faq) page.
~~~
### Run the app
Everything is in place to run the application with its contact editor.
@ -553,19 +627,20 @@ An issue arose [earlier](guide/ngmodule#import-name-conflict) when you declared
you already had a `HighlightDirective` class at the application level.
The selectors of the two directives both highlight the attached element with a different color.
<md-tab-group>
<md-tab label="src/app/highlight.directive.ts">
{@example 'ngmodule/ts/src/app/highlight.directive.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts">
</code-pane>
<md-tab label="src/app/contact/highlight.directive.ts">
{@example 'ngmodule/ts/src/app/contact/highlight.directive.ts'}
</md-tab>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Both directives are declared in this module so both directives are active.
@ -574,6 +649,9 @@ the directive that's declared later wins because its DOM changes overwrite the f
In this case, the contact's `HighlightDirective` makes the application title text blue
when it should stay gold.
~~~ {.l-sub-section}
The issue is that two different classes are trying to do the same thing.
It's OK to import the same directive class multiple times.
@ -582,6 +660,10 @@ Angular removes duplicate classes and only registers one of them.
But from Angular's perspective, two different classes, defined in different files, that have the same name
are not duplicates. Angular keeps both directives and
they take turns modifying the same HTML element.
~~~
At least the app still compiles.
If you define two different component classes with the same selector specifying the same element tag,
the compiler reports an error. It can't insert two components in the same DOM location.
@ -654,7 +736,9 @@ It's easy to refactor the contact material into a contact feature module.
Here's the new `ContactModule`:
{@example 'ngmodule/ts/src/app/contact/contact.module.2.ts'}
<code-example path="ngmodule/src/app/contact/contact.module.2.ts">
</code-example>
You copy from `AppModule` the contact-related import statements and `@NgModule` properties
that concern the contact, and paste them into `ContactModule`.
@ -693,19 +777,20 @@ Leave only the classes required at the application root level.
Then import the `ContactModule` so the app can continue to display the exported `ContactComponent`.
Here's the refactored version of the `AppModule` along with the previous version.
<md-tab-group>
<md-tab label="src/app/app.module.ts (v2)">
{@example 'ngmodule/ts/src/app/app.module.2.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts">
</code-pane>
<md-tab label="src/app/app.module.ts (v1)">
{@example 'ngmodule/ts/src/app/app.module.1b.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts">
</code-pane>
</md-tab-group>
</code-tabs>
### ImprovementsThere's a lot to like in the revised `AppModule`.
* It does not change as the _Contact_ domain grows.
@ -730,8 +815,15 @@ The Heroic Staffing Agency sample app has evolved.
It has two more modules, one for managing the heroes on staff and another for matching crises to the heroes.
Both modules are in the early stages of development.
Their specifics aren't important to the story and this page doesn't discuss every line of code.
~~~ {.l-sub-section}
Examine and download the complete source for this version from
the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example>Some facets of the current application merit discussion are as follows:
the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example>
~~~
Some facets of the current application merit discussion are as follows:
* The app has three feature modules: Contact, Hero, and Crisis.
* The Angular router helps users navigate among these modules.
@ -743,19 +835,30 @@ the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">li
The new `AppComponent` template has
a title, three links, and a `<router-outlet>`.
{@example 'ngmodule/ts/src/app/app.component.3.ts' region='template'}
<code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false">
</code-example>
The `<app-contact>` element is gone; you're routing to the _Contact_ page now.
The `AppModule` has changed modestly:
{@example 'ngmodule/ts/src/app/app.module.3.ts'}
<code-example path="ngmodule/src/app/app.module.3.ts">
</code-example>
~~~ {.l-sub-section}
Some file names bear a `.3` extension that indicates
a difference with prior or future versions.
The significant differences will be explained in due course.
<!-- CF: Can you be more specific here? Are the differences explained later in this page or in another page? -->
~~~
The module still imports `ContactModule` so that its routes and components are mounted when the app starts.
The module does _not_ import `HeroModule` or `CrisisModule`.
@ -767,7 +870,9 @@ that handles the app's routing concerns.
### App routing
{@example 'ngmodule/ts/src/app/app-routing.module.ts'}
<code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false">
</code-example>
The router is the subject of the [Routing & Navigation](guide/router) page, so this section skips many of the details and
concentrates on the intersection of NgModules and routing.
@ -784,18 +889,29 @@ You'll get to that file in a moment.
The remaining two routes use lazy loading syntax to tell the router where to find the modules:
{@example 'ngmodule/ts/src/app/app-routing.module.ts' region='lazy-routes'}
<code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false">
</code-example>
~~~ {.l-sub-section}
A lazy-loaded module location is a _string_, not a _type_.
In this app, the string identifies both the module _file_ and the module _class_,
the latter separated from the former by a `#`.
~~~
### RouterModule.forRoot
The `forRoot` static class method of the `RouterModule` with the provided configuration and
added to the `imports` array provides the routing concerns for the module.
{@example 'ngmodule/ts/src/app/app-routing.module.ts' region='forRoot'}
<code-example path="ngmodule/src/app/app-routing.module.ts" region="forRoot" linenums="false">
</code-example>
The returned `AppRoutingModule` class is a `Routing Module` containing both the `RouterModule` directives
and the dependency-injection providers that produce a configured `Router`.
@ -812,13 +928,17 @@ Never call `RouterModule.forRoot` in a feature-routing module.
Back in the root `AppModule`, add the `AppRoutingModule` to its `imports` list,
and the app is ready to navigate.
{@example 'ngmodule/ts/src/app/app.module.3.ts' region='imports'}
<code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false">
</code-example>
### Routing to a feature module
The `src/app/contact` folder holds a new file, `contact-routing.module.ts`.
It defines the `contact` route mentioned earlier and provides a `ContactRoutingModule` as follows:
{@example 'ngmodule/ts/src/app/contact/contact-routing.module.ts' region='routing'}
<code-example path="ngmodule/src/app/contact/contact-routing.module.ts" region="routing" linenums="false">
</code-example>
This time you pass the route list to the `forChild` method of the `RouterModule`.
The route list is only responsible for providing additional routes and is intended for feature modules.
@ -846,19 +966,20 @@ that has both shared [declarables](cookbook/ngmodule-faq) and services.
~~~
`ContactModule` has changed in two small but important ways.
<md-tab-group>
<md-tab label="src/app/contact/contact.module.3.ts">
{@example 'ngmodule/ts/src/app/contact/contact.module.3.ts' region='class'}
</md-tab>
<code-tabs>
<code-pane title="src/app/contact/contact.module.3.ts" path="ngmodule/src/app/contact/contact.module.3.ts" region="class">
</code-pane>
<md-tab label="src/app/contact/contact.module.2.ts">
{@example 'ngmodule/ts/src/app/contact/contact.module.2.ts' region='class'}
</md-tab>
<code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class">
</code-pane>
</md-tab-group>
</code-tabs>
* It imports the `ContactRoutingModule` object from `contact-routing.module.ts`.
* It no longer exports `ContactComponent`.
@ -935,7 +1056,9 @@ In the next section, [Shared modules](guide/ngmodule#shared-module "Shared modul
The `HeroModule` is a feature module like any other.
{@example 'ngmodule/ts/src/app/hero/hero.module.3.ts' region='class'}
<code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false">
</code-example>
It imports the `FormsModule` because the `HeroDetailComponent` template binds with `[(ngModel)]`.
It imports the `HeroRoutingModule` from `hero-routing.module.ts` just as `ContactModule` and `CrisisModule` do.
@ -964,7 +1087,9 @@ and share them with the modules that need them.
Here is the `SharedModule`:
{@example 'ngmodule/ts/src/app/shared/shared.module.ts'}
<code-example path="ngmodule/src/app/shared/shared.module.ts">
</code-example>
Note the following:
* It imports the `CommonModule` because its component needs common directives.
@ -1041,11 +1166,20 @@ Perform the following steps:
Most of this work is familiar. The interesting part is the `CoreModule`.
{@example 'ngmodule/ts/src/app/core/core.module.ts' region='v4'}
<code-example path="ngmodule/src/app/core/core.module.ts" region="v4">
</code-example>
~~~ {.l-sub-section}
You're importing some extra symbols from the Angular core library that you're not using yet.
They'll become relevant later in this page.The `@NgModule` metadata should be familiar.
They'll become relevant later in this page.
~~~
The `@NgModule` metadata should be familiar.
You declare the `TitleComponent` because this module owns it and you export it
because `AppComponent` (which is in `AppModule`) displays the title in its template.
`TitleComponent` needs the Angular `NgIf` directive that you import from `CommonModule`.
@ -1054,6 +1188,9 @@ because `AppComponent` (which is in `AppModule`) displays the title in its templ
making a singleton instance of the `UserService` available to any component that needs it,
whether that component is eagerly or lazily loaded.
~~~ {.l-sub-section}
### Why bother?
This scenario is clearly contrived.
The app is too small to worry about a single service file and a tiny, one-time component.
@ -1079,25 +1216,30 @@ Their _providers_ aren't shared.
We recommend collecting such single-use classes and hiding their details inside a `CoreModule`.
A simplified root `AppModule` imports `CoreModule` in its capacity as orchestrator of the application as a whole.
~~~
## Cleanup
Having refactored to a `CoreModule` and a `SharedModule`, it's time to clean up the other modules.
### A trimmer _AppModule_
Here is the updated `AppModule` paired with version 3 for comparison:
<md-tab-group>
<md-tab label="src/app/app.module.ts (v4)">
{@example 'ngmodule/ts/src/app/app.module.ts' region='v4'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4">
</code-pane>
<md-tab label="src/app/app.module.ts (v3)">
{@example 'ngmodule/ts/src/app/app.module.3.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts">
</code-pane>
</md-tab-group>
</code-tabs>
`AppModule` now has the following qualities:
* A little smaller because many `src/app/root` classes have moved to other modules.
@ -1107,19 +1249,20 @@ Here is the updated `AppModule` paired with version 3 for comparison:
### A trimmer _ContactModule_
Here is the new `ContactModule` paired with the prior version:
<md-tab-group>
<md-tab label="src/app/contact/contact.module.ts (v4)">
{@example 'ngmodule/ts/src/app/contact/contact.module.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts">
</code-pane>
<md-tab label="src/app/contact/contact.module.ts (v3)">
{@example 'ngmodule/ts/src/app/contact/contact.module.3.ts'}
</md-tab>
<code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts">
</code-pane>
</md-tab-group>
</code-tabs>
Notice the following:
* The `AwesomePipe` and `HighlightDirective` are gone.
@ -1146,22 +1289,35 @@ a simple object with the following properties:
* `providers`: the configured providers
The root `AppModule` imports the `CoreModule` and adds the `providers` to the `AppModule` providers.
~~~ {.l-sub-section}
More precisely, Angular accumulates all imported providers before appending the items listed in `@NgModule.providers`.
This sequence ensures that whatever you add explicitly to the `AppModule` providers takes precedence
over the providers of imported modules.Add a `CoreModule.forRoot` method that configures the core `UserService`.
over the providers of imported modules.
~~~
Add a `CoreModule.forRoot` method that configures the core `UserService`.
You've extended the core `UserService` with an optional, injected `UserServiceConfig`.
If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
{@example 'ngmodule/ts/src/app/core/user.service.ts' region='ctor'}
<code-example path="ngmodule/src/app/core/user.service.ts" region="ctor" linenums="false">
</code-example>
Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object:
{@example 'ngmodule/ts/src/app/core/core.module.ts' region='for-root'}
<code-example path="ngmodule/src/app/core/core.module.ts" region="for-root" linenums="false">
</code-example>
Lastly, call it within the `imports` list of the `AppModule`.
{@example 'ngmodule/ts/src/app/app.module.ts' region='import-for-root'}
<code-example path="ngmodule/src/app/app.module.ts" region="import-for-root" linenums="false">
</code-example>
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
@ -1196,7 +1352,9 @@ It looks like it is supposed to go to a specific question/section within the pag
You could hope that no developer makes that mistake.
Or you can guard against it and fail fast by adding the following `CoreModule` constructor.
{@example 'ngmodule/ts/src/app/core/core.module.ts' region='ctor'}
<code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false">
</code-example>
The constructor tells Angular to inject the `CoreModule` into itself.
That seems dangerously circular.

View File

@ -7,6 +7,9 @@ Recommended npm packages, and how to specify package dependencies.
@description
Angular applications and Angular itself depend upon features and functionality provided by a variety of third-party packages.
These packages are maintained and installed with the Node Package Manager (<a href="https://docs.npmjs.com/" target="_blank">npm</a>).
~~~ {.l-sub-section}
Node.js and npm are essential to Angular development.
<a href="https://docs.npmjs.com/getting-started/installing-node" target="_blank" title="Installing Node.js and updating npm">
@ -19,6 +22,10 @@ Older versions produce errors.
Consider using [nvm](https://github.com/creationix/nvm) for managing multiple
versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if
you already have projects running on your machine that use other versions of node and npm.
~~~
During [Setup](guide/setup), a <a href="https://docs.npmjs.com/files/package.json" target="_blank">package.json</a>
file is installed with a comprehensive starter set of
packages as specified in the `dependencies` and `devDependencies` sections.
@ -26,7 +33,14 @@ packages as specified in the `dependencies` and `devDependencies` sections.
You can use other packages but the packages in _this particular set_ work well together and include
everything you need to build and run the sample applications in this series.
Note: A cookbook or guide page may require an additional library such as *jQuery*.You'll install more than you need for the QuickStart guide.
~~~ {.l-sub-section}
Note: A cookbook or guide page may require an additional library such as *jQuery*.
~~~
You'll install more than you need for the QuickStart guide.
No worries!
You only serve to the client those packages that the application actually requests.
@ -102,7 +116,14 @@ Install these polyfills using the npm packages that Angular lists in the *peerDe
You must list these packages in the `dependencies` section of your own `package.json`.
For background on this requirement, see [Why peerDependencies?](guide/npm-packages#why-peer-dependencies).***core-js***: Patches the global context (window) with essential features of ES2015 (ES6).
~~~ {.l-sub-section}
For background on this requirement, see [Why peerDependencies?](guide/npm-packages#why-peer-dependencies).
~~~
***core-js***: Patches the global context (window) with essential features of ES2015 (ES6).
You may substitute an alternative polyfill that provides the same core APIs.
When these APIs are implemented by the major browsers, this dependency will become unnecessary.
@ -205,9 +226,15 @@ They leave you in control of package and version resolution.
It is your responsibility to list all *peer dependency* packages **among your own *devDependencies***.
~~~ {.l-sub-section}
#### The future of *peerDependencies*
The Angular polyfill dependencies are hard requirements. Currently, there is no way to make them optional.
However, there is an npm feature request for "optional peerDependencies," which would allow you to model this relationship better.
When this feature request is implemented, Angular will switch from *peerDependencies* to *optionalPeerDependencies* for all polyfills.
When this feature request is implemented, Angular will switch from *peerDependencies* to *optionalPeerDependencies* for all polyfills.
~~~

575
aio/content/guide/pipes.md Normal file
View File

@ -0,0 +1,575 @@
@title
Pipes
@intro
Pipes transform displayed values within a template.
@description
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
Getting data could be as simple as creating a local variable or as complex as streaming data over a WebSocket.
Once data arrive, you could push their raw `toString` values directly to the view,
but that rarely makes for a good user experience.
For example, in most use cases, users prefer to see a date in a simple format like
<samp>April 15, 1988</samp> rather than the raw string format
<samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>.
Clearly, some values benefit from a bit of editing. You may notice that you
desire many of the same transformations repeatedly, both within and across many applications.
You can almost think of them as styles.
In fact, you might like to apply them in your HTML templates as you do styles.
Introducing Angular pipes, a way to write display-value transformations that you can declare in your HTML.
You can run the <live-example></live-example> in Plunker and download the code from there.
## Using pipes
A pipe takes in data as input and transforms it to a desired output.
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" linenums="false">
</code-example>
Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false">
</code-example>
Inside the interpolation expression, you flow the component's `birthday` value through the
[pipe operator](guide/template-syntax) ( | ) to the [Date pipe](api/common/index/DatePipe-pipe)
function on the right. All pipes work this way.
~~~ {.l-sub-section}
The `Date` and `Currency` pipes need the *ECMAScript Internationalization API*.
Safari and other older browsers don't support it. You can add support with a polyfill.
<code-example language="html">
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt;
</code-example>
~~~
## Built-in pipes
Angular comes with a stock of pipes such as
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
They are all available for use in any template.
~~~ {.l-sub-section}
Read more about these and many other built-in pipes in the [pipes topics](api/#!?query=pipe) of the
[API Reference](api); filter for entries that include the word "pipe".
Angular doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in the [Appendix](guide/pipes#no-filter-pipe) of this page.
~~~
## Parameterizing a pipe
A pipe can accept any number of optional parameters to fine-tune its output.
To add parameters to a pipe, follow the pipe name with a colon ( : ) and then the parameter value
(such as `currency:'EUR'`). If the pipe accepts multiple parameters, separate the values with colons (such as `slice:1:5`)
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" linenums="false">
</code-example>
The parameter value can be any valid template expression,
(see the [Template expressions](guide/template-syntax) section of the
[Template Syntax](guide/template-syntax) page)
such as a string literal or a component property.
In other words, you can control the format through a binding the same way you control the birthday value through a binding.
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" linenums="false">
</code-example>
You also added a button to the template and bound its click event to the component's `toggleFormat()` method.
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" linenums="false">
</code-example>
As you click the button, the displayed date alternates between
"**<samp>04/15/1988</samp>**" and
"**<samp>Friday, April 15, 1988</samp>**".
<figure class='image-display'>
<img src='assets/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"> </img>
</figure>
~~~ {.l-sub-section}
Read more about the `DatePipe` format options in the [Date Pipe](api/common/index/DatePipe-pipe)
API Reference page.
~~~
## Chaining pipes
You can chain pipes together in potentially useful combinations.
In the following example, to display the birthday in uppercase,
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" linenums="false">
</code-example>
This example&mdash;which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**&mdash;chains
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" linenums="false">
</code-example>
## Custom pipes
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" linenums="false">
</code-example>
This pipe definition reveals the following key points:
* A pipe is a class decorated with pipe metadata.
* The pipe class implements the `PipeTransform` interface's `transform` method that
accepts an input value followed by optional parameters and returns the transformed value.
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
Your pipe has one such parameter: the `exponent`.
* To tell Angular that this is a pipe, you apply the
`@Pipe` #{_decorator}, which you import from the core Angular library.
* The `@Pipe` #{_decorator} allows you to define the
pipe name that you'll use within template expressions. It must be a valid JavaScript identifier.
Your pipe's name is `exponentialStrength`.
~~~ {.l-sub-section}
### The *PipeTransform* interface
The `transform` method is essential to a pipe.
The `PipeTransform` *interface* defines that method and guides both tooling and the compiler.
Technically, it's optional; Angular looks for and executes the `transform` method regardless.
~~~
Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" linenums="false">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/pipes/power-booster.png' alt="Power Booster"> </img>
</figure>
Note the following:
* You use your custom pipe the same way you use built-in pipes.
* You must include your pipe in the `!{_decls}` #{_array} of the `!{_appMod}`.
~~~ {.callout.is-helpful}
<header>
Remember the
</header>
You must manually register custom pipes.
If you don't, Angular reports an error.
In the previous example, you didn't list the `DatePipe` because all
Angular built-in pipes are pre-registered.
~~~
To probe the behavior in the <live-example></live-example>,
change the value and optional exponent in the template.
## Power Boost Calculator
It's not much fun updating the template to test the custom pipe.
Upgrade the example to a "Power Boost Calculator" that combines
your pipe and two-way data binding with `ngModel`.
<code-example path="pipes/src/app/power-boost-calculator.component.ts">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"> </img>
</figure>
{@a change-detection}
## Pipes and change detection
Angular looks for changes to data-bound values through a *change detection* process that runs after every DOM event:
every keystroke, mouse move, timer tick, and server response. This could be expensive.
Angular strives to lower the cost whenever possible and appropriate.
Angular picks a simpler, faster change detection algorithm when you use a pipe.
### No pipe
In the next example, the component uses the default, aggressive change detection strategy to monitor and update
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" linenums="false">
</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" linenums="false">
</code-example>
You can add heroes and Angular updates the display when you do.
If you click the `reset` button, Angular replaces `heroes` with a new #{_array} of the original heroes and updates the display.
If you added the ability to remove or change a hero, Angular would detect those changes and update the display as well.
### Flying-heroes pipe
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" linenums="false">
</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" linenums="false">
</code-example>
Notice the odd behavior in the <live-example></live-example>:
when you add flying heroes, none of them are displayed under "Heroes who fly."
Although you're not getting the behavior you want, Angular isn't broken.
It's just using a different change-detection algorithm that ignores changes to the list or any of its items.
Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false">
</code-example>
You add the hero into the `heroes` #{_array}. The reference to the #{_array} hasn't changed.
It's the same #{_array}. That's all Angular cares about. From its perspective, *same #{_array}, no change, no display update*.
To fix that, create an #{_array} with the new hero appended and assign that to `heroes`.
This time Angular detects that the #{_array} reference has changed.
It executes the pipe and updates the display with the new #{_array}, which includes the new flying hero.
If you *mutate* the #{_array}, no pipe is invoked and the display isn't updated;
if you *replace* the #{_array}, the pipe executes and the display is updated.
The Flying Heroes application extends the
code with checkbox switches and additional displays to help you experience these effects.
<figure class='image-display'>
<img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img>
</figure>
Replacing the #{_array} is an efficient way to signal Angular to update the display.
When do you replace the #{_array}? When the data change.
That's an easy rule to follow in *this* example
where the only way to change the data is by adding a hero.
More often, you don't know when the data have changed,
especially in applications that mutate data in many ways,
perhaps in application locations far away.
A component in such an application usually can't know about those changes.
Moreover, it's unwise to distort the component design to accommodate a pipe.
Strive to keep the component class independent of the HTML.
The component should be unaware of pipes.
For filtering flying heroes, consider an *impure pipe*.
## Pure and impure pipes
There are two categories of pipes: *pure* and *impure*.
Pipes are pure by default. Every pipe you've seen so far has been pure.
You make a pipe impure by setting its pure flag to false. You could make the `FlyingHeroesPipe`
impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false">
</code-example>
Before doing that, understand the difference between pure and impure, starting with a pure pipe.
### Pure pipes
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
A pure change is either a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`)
or a changed object reference (`Date`, `Array`, `Function`, `Object`).
Angular ignores changes within (composite) objects.
It won't call a pure pipe if you change an input month, add to an input #{_array}, or update an input object property.
This may seem restrictive but it's also fast.
An object reference check is fast&mdash;much faster than a deep check for
differences&mdash;so Angular can quickly determine if it can skip both the
pipe execution and a view update.
For this reason, a pure pipe is preferable when you can live with the change detection strategy.
When you can't, you *can* use the impure pipe.
~~~ {.l-sub-section}
Or you might not use a pipe at all.
It may be better to pursue the pipe's purpose with a property of the component,
a point that's discussed later in this page.
~~~
### Impure pipes
Angular executes an *impure pipe* during every component change detection cycle.
An impure pipe is called often, as often as every keystroke or mouse-move.
With that concern in mind, implement an impure pipe with great care.
An expensive, long-running pipe could destroy the user experience.
<a id="impure-flying-heroes"></a>
### An impure *FlyingHeroesPipe*
A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
The complete implementation is as follows:
<code-tabs>
<code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure">
</code-pane>
<code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure">
</code-pane>
</code-tabs>
You inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
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" title="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" linenums="false" title="src/app/flying-heroes (_region)" region="_region">
</code-example>
The only substantive change is the pipe in the template.
You can confirm in the <live-example></live-example> that the _flying heroes_
display updates as you add heroes, even when you mutate the `heroes` #{_array}.
<h3 id='async-pipe'>
The impure <i> AsyncPipe </i>
</h3>
The Angular `AsyncPipe` is an interesting example of an impure pipe.
The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input
and subscribes to the input automatically, eventually returning the emitted values.
The `AsyncPipe` is also stateful.
The pipe maintains a subscription to the input `#{_Observable}` and
keeps delivering values from that `#{_Observable}` as they arrive.
This next example binds an `#{_Observable}` of message strings
(`message#{_dollar}`) to a view with the `async` pipe.
<code-example path="pipes/src/app/hero-async-message.component.ts">
</code-example>
The Async pipe saves boilerplate in the component code.
The component doesn't have to subscribe to the async data source,
extract the resolved values and expose them for binding,
and have to unsubscribe when it's destroyed
(a potent source of memory leaks).
### An impure caching pipe
Write one more impure pipe, a pipe that makes an HTTP request.
Remember that impure pipes are called every few milliseconds.
If you're not careful, this pipe will punish the server with requests.
In the following code, the pipe only calls the server when the request URL changes and it caches the server response.
The code<span if-docs="ts"> uses the [Angular http](guide/server-communication) client to retrieve data</span>:
<code-example path="pipes/src/app/fetch-json.pipe.ts">
</code-example>
Now demonstrate it in a harness component whose template defines two bindings to this pipe,
both requesting the heroes from the `heroes.json` file.
<code-example path="pipes/src/app/hero-list.component.ts">
</code-example>
The component renders as the following:
<figure class='image-display'>
<img src='assets/images/devguide/pipes/hero-list.png' alt="Hero List"> </img>
</figure>
A breakpoint on the pipe's request for data shows the following:
* Each binding gets its own pipe instance.
* Each pipe instance caches its own URL and data.
* Each pipe instance only calls the server once.
### *JsonPipe*
In the previous code sample, the second `fetch` pipe binding demonstrates more pipe chaining.
It displays the same hero data in JSON format by chaining through to the built-in `JsonPipe`.
~~~ {.callout.is-helpful}
<header>
Debugging with the json pipe
</header>
The [JsonPipe](api/common/index/JsonPipe-pipe)
provides an easy way to diagnosis a mysteriously failing data binding or
inspect an object for future binding.
~~~
{@a pure-pipe-pure-fn}
### Pure pipes and pure functions
A pure pipe uses pure functions.
Pure functions process inputs and return values without detectable side effects.
Given the same input, they should always return the same output.
The pipes discussed earlier in this page are implemented with pure functions.
The built-in `DatePipe` is a pure pipe with a pure function implementation.
So are the `ExponentialStrengthPipe` and `FlyingHeroesPipe`.
A few steps back, you reviewed the `FlyingHeroesImpurePipe`&mdash;an impure pipe with a pure function.
But always implement a *pure pipe* with a *pure function*.
Otherwise, you'll see many console errors regarding expressions that changed after they were checked.
## Next steps
Pipes are a great way to encapsulate and share common display-value
transformations. Use them like styles, dropping them
into your template's expressions to enrich the appeal and usability
of your views.
Explore Angular's inventory of built-in pipes in the [API Reference](api/#!?query=pipe).
Try writing a custom pipe and perhaps contributing it to the community.
{@a no-filter-pipe}
## Appendix: No *FilterPipe* or *OrderByPipe*
Angular doesn't provide pipes for filtering or sorting lists.
Developers familiar with AngularJS know these as `filter` and `orderBy`.
There are no equivalents in Angular.
This isn't an oversight. Angular doesn't offer such pipes because
they perform poorly and prevent aggressive minification.
Both `filter` and `orderBy` require parameters that reference object properties.
Earlier in this page, you learned that such pipes must be [impure](guide/pipes#pure-and-impure-pipes) and that
Angular calls impure pipes in almost every change-detection cycle.
Filtering and especially sorting are expensive operations.
The user experience can degrade severely for even moderate-sized lists when Angular calls these pipe methods many times per second.
`filter` and `orderBy` have often been abused in AngularJS apps, leading to complaints that Angular itself is slow.
That charge is fair in the indirect sense that AngularJS prepared this performance trap
by offering `filter` and `orderBy` in the first place.
The minification hazard is also compelling, if less obvious. Imagine a sorting pipe applied to a list of heroes.
The list might be sorted by hero `name` and `planet` of origin properties in the following way:
<code-example language="html">
&lt;!-- NOT REAL CODE! -->
&lt;div *ngFor="let hero of heroes | orderBy:'name,planet'">&lt;/div>
</code-example>
You identify the sort fields by text strings, expecting the pipe to reference a property value by indexing
(such as `hero['name']`).
Unfortunately, aggressive minification manipulates the `Hero` property names so that `Hero.name` and `Hero.planet`
become something like `Hero.a` and `Hero.b`. Clearly `hero['name']` doesn't work.
While some may not care to minify this aggressively,
the Angular product shouldn't prevent anyone from minifying aggressively.
Therefore, the Angular team decided that everything Angular provides will minify safely.
The Angular team and many experienced Angular developers strongly recommend moving
filtering and sorting logic into the component itself.
The component can expose a `filteredHeroes` or `sortedHeroes` property and take control
over when and how often to execute the supporting logic.
Any capabilities that you would have put in a pipe and shared across the app can be
written in a filtering/sorting service and injected into the component.
If these performance and minification considerations don't apply to you, you can always create your own such pipes
(similar to the [FlyingHeroesPipe](guide/pipes#impure-flying-heroes)) or find them in the community.

View File

@ -159,7 +159,9 @@ You'll need a `hero` class and some hero data.
Create a new `data-model.ts` file in the `app` directory and copy the content below into it.
{@example 'reactive-forms/ts/src/app/data-model.ts'}
<code-example path="reactive-forms/src/app/data-model.ts" linenums="false">
</code-example>
The file exports two classes and two constants. The `Address`
and `Hero` classes define the application _data model_.
@ -173,16 +175,16 @@ Make a new file called
`hero-detail.component.ts` in the `app` directory and import these symbols:
{@example 'reactive-forms/ts/src/app/hero-detail-1.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" linenums="false">
</code-example>
Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata:
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='metadata'}
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" linenums="false">
The `moduleId: module.id` lets you use
[component-relative paths](cookbook/component-relative-paths) in file URLs
such as when specifying the `templateUrl`.
</code-example>
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
`FormControl` is a directive that allows you to create and manage
@ -190,7 +192,9 @@ a `FormControl` instance directly.
{@example 'reactive-forms/ts/src/app/hero-detail-1.component.ts' region='v1'}
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" linenums="false">
</code-example>
Here you are creating a `FormControl` called `name`.
It will be bound in the template to an HTML `input` box for the hero name.
@ -201,10 +205,17 @@ the initial data value, an array of validators, and an array of async validators
This simple control doesn't have data or validators.
In real apps, most form controls have both.
~~~ {.l-sub-section}
This guide touches only briefly on `Validators`. For an in-depth look at them,
read the [Form Validation](cookbook/form-validation) cookbook.
~~~
{@a create-template}
## Create the template
@ -212,19 +223,28 @@ read the [Form Validation](cookbook/form-validation) cookbook.
Now create the component's template, `src/app/hero-detail.component.html`, with the following markup.
{@example 'reactive-forms/ts/src/app/hero-detail-1.component.html' region='simple-control'}
<code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false">
</code-example>
To let Angular know that this is the input that you want to
associate to the `name` `FormControl` in the class,
you need `[formControl]="name"` in the template on the `<input>`.
~~~ {.l-sub-section}
Disregard the `form-control` _CSS_ class. It belongs to the
<a href="http://getbootstrap.com/" target="_blank" title="Bootstrap CSS">Bootstrap CSS library</a>,
not Angular.
It _styles_ the form but in no way impacts the logic of the form.
~~~
{@a import}
## Import the _ReactiveFormsModule_
@ -239,7 +259,9 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
1. Add `HeroDetailComponent` to the declarations array.
{@example 'reactive-forms/ts/src/app/app.module.ts' region='v1'}
<code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false">
</code-example>
@ -248,7 +270,9 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
## Display the _HeroDetailComponent_
Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
{@example 'reactive-forms/ts/src/app/app.component.1.ts'}
<code-example path="reactive-forms/src/app/app.component.1.ts" linenums="false">
</code-example>
@ -280,7 +304,9 @@ You used bootstrap CSS classes in the template HTML of both the `AppComponent` a
Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
{@example 'reactive-forms/ts/src/index.html' region='bootstrap'}
<code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false">
</code-example>
Now that everything is wired up, the browser should display something like this:
@ -298,18 +324,24 @@ This is simple to do. To add a `FormGroup`, add it to the imports section
of `hero-detail.component.ts`:
{@example 'reactive-forms/ts/src/app/hero-detail-2.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false">
</code-example>
In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows:
{@example 'reactive-forms/ts/src/app/hero-detail-2.component.ts' region='v2'}
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" linenums="false">
</code-example>
Now that you've made changes in the class, they need to be reflected in the
template. Update `hero-detail.component.html` by replacing it with the following.
{@example 'reactive-forms/ts/src/app/hero-detail-2.component.html' region='basic-form'}
<code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false">
</code-example>
Notice that now the single input is in a `form` element. The `novalidate`
attribute in the `<form>` element prevents the browser
@ -333,12 +365,19 @@ in the class. This syntax tells Angular to look for the parent
`FormGroup`, in this case `heroForm`, and then _inside_ that group
to look for a `FormControl` called `name`.
~~~ {.l-sub-section}
Disregard the `form-group` _CSS_ class. It belongs to the
<a href="http://getbootstrap.com/" target="_blank" title="Bootstrap CSS">Bootstrap CSS library</a>,
not Angular.
Like the `form-control` class, it _styles_ the form
but in no way impacts its logic.
~~~
The form looks great. But does it work?
When the user enters a name, where does the value go?
@ -351,7 +390,9 @@ To see the form model, add the following line after the
closing `form` tag in the `hero-detail.component.html`:
{@example 'reactive-forms/ts/src/app/hero-detail-3.component.html' region='form-value-json'}
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example>
The `heroForm.value` returns the _form model_.
Piping it through the `JsonPipe` renders the model as JSON in the browser:
@ -380,7 +421,9 @@ clutter by handling details of control creation for you.
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
{@example 'reactive-forms/ts/src/app/hero-detail-3a.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false">
</code-example>
Use it now to refactor the `HeroDetailComponent` into something that's a little easier to read and write,
by following this plan:
@ -392,7 +435,9 @@ by following this plan:
The revised `HeroDetailComponent` looks like this:
{@example 'reactive-forms/ts/src/app/hero-detail-3a.component.ts' region='v3a'}
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false">
</code-example>
`FormBuilder.group` is a factory method that creates a `FormGroup`. &nbsp;
`FormBuilder.group` takes an object whose keys and values are `FormControl` names and their definitions.
@ -409,7 +454,9 @@ demonstrates the simplicity of using `Validators.required` in reactive forms.
First, import the `Validators` symbol.
{@example 'reactive-forms/ts/src/app/hero-detail-3.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false">
</code-example>
To make the `name` `FormControl` required, replace the `name`
property in the `FormGroup` with an array.
@ -417,14 +464,25 @@ The first item is the initial value for `name`;
the second is the required validator, `Validators.required`.
{@example 'reactive-forms/ts/src/app/hero-detail-3.component.ts' region='required'}
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" linenums="false">
</code-example>
~~~ {.l-sub-section}
Reactive validators are simple, composable functions.
Configuring validation is harder in template-driven forms where you must wrap validators in a directive. Update the diagnostic message at the bottom of the template to display the form's validity status.
Configuring validation is harder in template-driven forms where you must wrap validators in a directive.
~~~
Update the diagnostic message at the bottom of the template to display the form's validity status.
{@example 'reactive-forms/ts/src/app/hero-detail-3.component.html' region='form-value-json'}
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example>
The browser displays the following:
@ -448,18 +506,24 @@ A hero has an address, a super power and sometimes a sidekick too.
The address has a state property. The user will select a state with a `<select>` box and you'll populate
the `<option>` elements with states. So import `states` from `data-model.ts`.
{@example 'reactive-forms/ts/src/app/hero-detail-4.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" linenums="false">
</code-example>
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
{@example 'reactive-forms/ts/src/app/hero-detail-4.component.ts' region='v4'}
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false">
</code-example>
Then add corresponding markup in `hero-detail.component.html`
within the `form` element.
{@example 'reactive-forms/ts/src/app/hero-detail-4.component.html'}
<code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false">
</code-example>
@ -508,7 +572,9 @@ Let that be the parent `FormGroup`.
Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls;
assign the result to a new `address` property of the parent `FormGroup`.
{@example 'reactive-forms/ts/src/app/hero-detail-5.component.ts' region='v5'}
<code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" linenums="false">
</code-example>
Youve changed the structure of the form controls in the component class;
you must make corresponding adjustments to the component template.
@ -521,7 +587,9 @@ To make this change visually obvious, slip in an `<h4>` header near the top with
The new _address_ HTML looks like this:
{@example 'reactive-forms/ts/src/app/hero-detail-5.component.html' region='add-group'}
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false">
</code-example>
After these changes, the JSON output in the browser shows the revised _form model_
with the nested address `FormGroup`:
@ -546,12 +614,16 @@ page by adding the following to the template,
immediately after the `{{form.value | json}}` interpolation as follows:
{@example 'reactive-forms/ts/src/app/hero-detail-5.component.html' region='inspect-value'}
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" linenums="false">
</code-example>
To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control.
{@example 'reactive-forms/ts/src/app/hero-detail-5.component.html' region='inspect-child-control'}
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false">
</code-example>
You can use this technique to display _any_ property of a `FormControl`
such as one of the following:
@ -692,12 +764,16 @@ In this `HeroDetailComponent`, the two models are quite close.
Recall the definition of `Hero` in `data-model.ts`:
{@example 'reactive-forms/ts/src/app/data-model.ts' region='model-classes'}
<code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false">
</code-example>
Here, again, is the component's `FormGroup` definition.
{@example 'reactive-forms/ts/src/app/hero-detail-6.component.ts' region='hero-form-model'}
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" linenums="false">
</code-example>
There are two significant differences between these models:
@ -710,11 +786,15 @@ Nonetheless, the two models are pretty close in shape and you'll see in a moment
to the _form model_ with the `patchValue` and `setValue` methods.
Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows:
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='address-form-group'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example>
Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes:
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='import-address'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" linenums="false">
</code-example>
@ -730,7 +810,9 @@ With **`setValue`**, you assign _every_ form control value _at once_
by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`.
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='set-value'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false">
</code-example>
The `setValue` method checks the data object thoroughly before assigning any form control values.
@ -748,7 +830,9 @@ because its shape is similar to the component's `FormGroup` structure.
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all.
This explains the conditional setting of the `address` property in the data object argument:
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='set-value-address'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" linenums="false">
</code-example>
### _patchValue_
With **`patchValue`**, you can assign values to specific controls in a `FormGroup`
@ -756,7 +840,9 @@ by supplying an object of key/value pairs for just the controls of interest.
This example sets only the form's `name` control.
{@example 'reactive-forms/ts/src/app/hero-detail-6.component.ts' region='patch-value'}
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false">
</code-example>
With **`patchValue`** you have more flexibility to cope with wildly divergent data and form models.
But unlike `setValue`, `patchValue` cannot check for missing control
@ -773,7 +859,9 @@ When the user clicks on a hero, the list component passes the selected hero into
by binding to its `hero` input property.
{@example 'reactive-forms/ts/src/app/hero-list.component.1.html'}
<code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false">
</code-example>
In this approach, the value of `hero` in the `HeroDetailComponent` changes
every time the user selects a new hero.
@ -781,19 +869,25 @@ You should call _setValue_ in the [ngOnChanges](guide/lifecyle-hooks)
hook, which Angular calls whenever the input `hero` property changes
as the following steps demonstrate.
First, import the `ngOnChanges` and `import` symbol in `hero-detail.component.ts`.
First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
{@example 'reactive-forms/ts/src/app/hero-detail-6.component.ts' region='import-input'}
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" linenums="false">
</code-example>
Add the `hero` input property.
{@example 'reactive-forms/ts/src/app/hero-detail-6.component.ts' region='hero'}
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false">
</code-example>
Add the `ngOnChanges` method to the class as follows:
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='ngOnChanges-1'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" linenums="false">
</code-example>
### _reset_ the form flags
@ -802,13 +896,17 @@ control values from the previous hero are cleared and
status flags are restored to the _pristine_ state.
You could call `reset` at the top of `ngOnChanges` like this.
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='reset'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false">
</code-example>
The `reset` method has an optional `state` value so you can reset the flags _and_ the control values at the same.
Internally, `reset` passes the argument to `setValue`.
A little refactoring and `ngOnChanges` becomes this:
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='ngOnChanges'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" linenums="false">
</code-example>
@ -864,7 +962,9 @@ An Angular `FormArray` can display an array of _address_ `FormGroups`.
To get access to the `FormArray` class, import it into `hero-detail.component.ts`:
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='imports'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false">
</code-example>
To _work_ with a `FormArray` you do the following:
1. Define the items (`FormControls` or `FormGroups`) in the array.
@ -877,7 +977,9 @@ let the user add or modify addresses (removing addresses is your homework).
Youll need to redefine the form model in the `HeroDetailComponent` constructor,
which currently only displays the first hero address in an _address_ `FormGroup`.
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='address-form-group'}
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example>
### From _address_ to _secret lairs_
@ -885,7 +987,9 @@ From the user's point of view, heroes don't have _addresses_.
_Addresses_ are for mere mortals. Heroes have _secret lairs_!
Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition:
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='secretLairs-form-array'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" linenums="false">
</code-example>
@ -913,7 +1017,9 @@ the parent `HeroListComponent` sets the `HeroListComponent.hero` input property
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
initialized by an array of hero address `FormGroups`.
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='set-addresses'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" linenums="false">
</code-example>
Notice that you replace the previous `FormArray` with the **`FormGroup.setControl` method**, not with `setValue`.
You're replacing a _control_, not the _value_ of a control.
@ -926,7 +1032,9 @@ The `HeroDetailComponent` should be able to display, add, and remove items from
Use the `FormGroup.get` method to acquire a reference to that `FormArray`.
Wrap the expression in a `secretLairs` convenience property for clarity and re-use.
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='get-secret-lairs'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" linenums="false">
</code-example>
### Display the _FormArray_
@ -950,22 +1058,30 @@ You'll re-use that index to compose a unique label for each address.
Here's the skeleton for the _secret lairs_ section of the HTML template:
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.html' region='form-array-skeleton'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false">
</code-example>
Here's the complete template for the _secret lairs_ section:
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.html' region='form-array'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array">
</code-example>
### Add a new lair to the _FormArray_
Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it.
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='add-lair'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false">
</code-example>
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method.
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.html' region='add-lair'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false">
</code-example>
@ -1015,16 +1131,22 @@ You don't need to know much about RxJS `Observable` to monitor form control valu
Add the following method to log changes to the value of the _name_ `FormControl`.
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='log-name-change'}
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false">
</code-example>
Call it in the constructor, after creating the form.
{@example 'reactive-forms/ts/src/app/hero-detail-8.component.ts' region='ctor'}
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false">
</code-example>
The `logNameChange` method pushes name-change values into a `nameChangeLog` array.
Display that array at the bottom of the component template with this `*ngFor` binding:
{@example 'reactive-forms/ts/src/app/hero-detail.component.html' region='name-change-log'}
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" linenums="false">
</code-example>
Return to the browser, select a hero (e.g, "Magneta"), and start typing in the _name_ input box.
You should see a new name in the log after each keystroke.
@ -1054,15 +1176,22 @@ In this sample application, when the user submits the form,
the `HeroDetailComponent` will pass an instance of the hero _data model_
to a save method on the injected `HeroService`.
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='on-submit'}
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" linenums="false">
</code-example>
This original `hero` had the pre-save values. The user's changes are still in the _form model_.
So you create a new `hero` from a combination of original hero values (the `hero.id`)
and deep copies of the changed form model values, using the `prepareSaveHero` helper.
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='prepare-save-hero'}
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false">
</code-example>
~~~ {.l-sub-section}
**Address deep copy**
@ -1072,17 +1201,25 @@ as the lairs in the `formModel.secretLairs`.
A user's subsequent changes to a lair street would mutate an address street in the `saveHero`.
The `prepareSaveHero` method makes copies of the form model's `secretLairs` objects so that can't happen.
~~~
### Revert (cancel changes)
The user cancels changes and reverts the form to the original state by pressing the _Revert_ button.
Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='revert'}
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false">
</code-example>
### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template:
{@example 'reactive-forms/ts/src/app/hero-detail.component.html' region='buttons'}
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" linenums="false">
</code-example>
The buttons are disabled until the user "dirties" the form by changing a value in any of its form controls (`heroForm.dirty`).
@ -1111,49 +1248,50 @@ This page covered:
{@a source-code}
The key files of the final version are as follows:
<md-tab-group>
<md-tab label="src/app/app.component.ts">
{@example 'reactive-forms/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts">
</code-pane>
<md-tab label="src/app/app.module.ts">
{@example 'reactive-forms/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts">
</code-pane>
<md-tab label="src/app/hero-detail.component.ts">
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts'}
</md-tab>
<code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts">
</code-pane>
<md-tab label="src/app/hero-detail.component.html">
{@example 'reactive-forms/ts/src/app/hero-detail.component.html'}
</md-tab>
<code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html">
</code-pane>
<md-tab label="src/app/hero-list.component.html">
{@example 'reactive-forms/ts/src/app/hero-list.component.html'}
</md-tab>
<code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html">
</code-pane>
<md-tab label="src/app/hero-list.component.ts">
{@example 'reactive-forms/ts/src/app/hero-list.component.ts'}
</md-tab>
<code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts">
</code-pane>
<md-tab label="src/app/data-model.ts">
{@example 'reactive-forms/ts/src/app/data-model.ts'}
</md-tab>
<code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts">
</code-pane>
<md-tab label="src/app/hero.service.ts">
{@example 'reactive-forms/ts/src/app/hero.service.ts'}
</md-tab>
<code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts">
</code-pane>
</md-tab-group>
</code-tabs>
You can download the complete source for all steps in this guide
from the <live-example title="Reactive Forms Demo in Plunker">Reactive Forms Demo</live-example> live example.

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ Security
Developing for content security in Angular applications.
@description
This page describes Angular's built-in
protections against common web-application vulnerabilities and attacks such as cross-site
scripting attacks. It doesn't cover application-level security, such as authentication (_Who is
@ -93,7 +94,9 @@ 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:
{@example 'security/ts/src/app/inner-html-binding.component.html'}
<code-example path="security/src/app/inner-html-binding.component.html">
</code-example>
Interpolated content is always escaped&mdash;the HTML isn't interpreted and the browser displays
angle brackets in the element's text content.
@ -101,6 +104,20 @@ angle brackets in the element's text content.
For the HTML to be interpreted, bind it to an HTML property such as `innerHTML`. But binding
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" title="src/app/inner-html-binding.component.ts (class)" region="class">
</code-example>
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>`
tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element.
<figure class='image-display'>
<img src='assets/images/devguide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'> </img>
</figure>
### Avoid direct use of the DOM APIs
The built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
@ -136,6 +153,163 @@ the server. Don't generate Angular templates on the server side using a templati
carries a high risk of introducing template-injection vulnerabilities.
<h2 id='bypass-security-apis'>
Trusting safe values
</h2>
Sometimes applications genuinely need to include executable code, display an `<iframe>` from some
URL, or construct potentially dangerous URLs. To prevent automatic sanitization in any of these
situations, you can tell Angular that you inspected a value, checked how it was generated, and made
sure it will always be secure. But *be careful*. If you trust a value that might be malicious, you
are introducing a security vulnerability into your application. If in doubt, find a professional
security reviewer.
To mark a value as trusted, inject `DomSanitizer` and call one of the
following methods:
* `bypassSecurityTrustHtml`
* `bypassSecurityTrustScript`
* `bypassSecurityTrustStyle`
* `bypassSecurityTrustUrl`
* `bypassSecurityTrustResourceUrl`
Remember, whether a value is safe depends on context, so choose the right context for
your intended use of the value. Imagine that the following template needs to bind a URL to a
`javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL">
</code-example>
Normally, Angular automatically sanitizes the URL, disables the dangerous code, and
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" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'> </img>
</figure>
If you need to convert user input into a trusted value, use a
controller method. The following template allows users to enter a YouTube video ID and load the
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
context, because an untrusted source can, for example, smuggle in file downloads that unsuspecting users
could execute. So call a method on the controller to construct a trusted video URL, which causes
Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example>
<h2 id='http'>
HTTP-level vulnerabilities
</h2>
Angular has built-in support to help prevent two common HTTP vulnerabilities, cross-site request
forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily
on the server side, but Angular provides helpers to make integration on the client side easier.
<h3 id='xsrf'>
Cross-site request forgery
</h3>
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
a different web page (such as `evil.com`) with malignant code that secretly sends a malicious request
to the application's web server (such as `example-bank.com`).
Assume the user is logged into the application at `example-bank.com`.
The user opens an email and clicks a link to `evil.com`, which opens in a new tab.
The `evil.com` page immediately sends a malicious request to `example-bank.com`.
Perhaps it's a request to transfer money from the user's account to the attacker's account.
The browser automatically sends the `example-bank.com` cookies (including the authentication cookie) with this request.
If the `example-bank.com` server lacks XSRF protection, it can't tell the difference between a legitimate
request from the application and the forged request from `evil.com`.
To prevent this, the application must ensure that a user request originates from the real
application, not from a different site.
The server and client must cooperate to thwart this attack.
In a common anti-XSRF technique, the application server sends a randomly
generated authentication token in a cookie.
The client code reads the cookie and adds a custom request header with the token in all subsequent requests.
The server compares the received cookie value to the request header value and rejects the request if the values are missing or don't match.
This technique is effective because all browsers implement the _same origin policy_. Only code from the website
on which cookies are set can read the cookies from that site and set custom headers on requests to that site.
That means only your application can read this cookie token and set the custom header. The malicious code on `evil.com` can't.
Angular's `http` has built-in support for the client-side half of this technique in its `XSRFStrategy`.
The default `CookieXSRFStrategy` is turned on automatically.
Before sending an HTTP request, the `CookieXSRFStrategy` looks for a cookie called `XSRF-TOKEN` and
sets a header named `X-XSRF-TOKEN` with the value of that cookie.
The server must do its part by setting the
initial `XSRF-TOKEN` cookie and confirming that each subsequent state-modifying request
includes a matching `XSRF-TOKEN` cookie and `X-XSRF-TOKEN` header.
XSRF/CSRF tokens should be unique per user and session, have a large random value generated by a
cryptographically secure random number generator, and expire in a day or two.
Your server may use a different cookie or header name for this purpose.
An Angular application can customize cookie and header names by providing its own `CookieXSRFStrategy` values.
<code-example language="typescript">
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name') }
</code-example>
Or you can implement and provide an entirely custom `XSRFStrategy`:
<code-example language="typescript">
{ provide: XSRFStrategy, useClass: MyXSRFStrategy }
</code-example>
For information about CSRF at the Open Web Application Security Project (OWASP), see
<a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29" target="_blank">Cross-Site Request Forgery (CSRF)</a> and
<a href="https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet" target="_blank">Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet</a>.
The Stanford University paper
<a href="https://seclab.stanford.edu/websec/csrf/csrf.pdf" target="_blank">Robust Defenses for Cross-Site Request Forgery</a> is a rich source of detail.
See also Dave Smith's easy-to-understand
<a href="https://www.youtube.com/watch?v=9inczw6qtpY" target="_blank" title="Cross Site Request Funkery Securing Your Angular Apps From Evil Doers">talk on XSRF at AngularConnect 2016</a>.
<h3 id='xssi'>
Cross-site script inclusion (XSSI)
</h3>
Cross-site script inclusion, also known as JSON vulnerability, can allow an attacker's website to
read data from a JSON API. The attack works on older browsers by overriding native JavaScript
object constructors, and then including an API URL using a `<script>` tag.
This attack is only successful if the returned JSON is executable as JavaScript. Servers can
prevent an attack by prefixing all JSON responses to make them non-executable, by convention, using the
well-known string `")]}',\n"`.
Angular's `Http` library recognizes this convention and automatically strips the string
`")]}',\n"` from all responses before further parsing.
For more information, see the XSSI section of this [Google web security blog
post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html).
<h2 id='code-review'>
Auditing Angular applications
</h2>

View File

@ -5,9 +5,17 @@ HTTP Client
Use an HTTP Client to talk to a remote server.
@description
[HTTP](https://tools.ietf.org/html/rfc2616) is the primary protocol for browser/server communication.
~~~ {.l-sub-section}
The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology;
it isn't covered in this page.Modern browsers support two HTTP-based APIs:
it isn't covered in this page.
~~~
Modern browsers support two HTTP-based APIs:
[XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
@ -60,9 +68,16 @@ A <live-example>live example</live-example> illustrates these topics.
# Demos
This page describes server communication with the help of the following demos:
- [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client).
- [Fall back to !{_Promise}s](guide/server-communication#promises).
- [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors).
- [More fun with Observables](guide/server-communication#more-observables).
The root `AppComponent` orchestrates these demos:
{@example 'server-communication/ts/src/app/app.component.ts'}
<code-example path="server-communication/src/app/app.component.ts">
</code-example>
# Providing HTTP services
@ -73,22 +88,42 @@ The !{_Angular_Http} client communicates with the server using a familiar HTTP r
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
Before you can use the `!{_Http}` client, you need to register it as a service provider with the dependency injection system.
~~~ {.l-sub-section}
Read about providers in the [Dependency Injection](guide/dependency-injection) page.
~~~
Register providers by importing other NgModules to the root NgModule in `app.module.ts`.
{@example 'server-communication/ts/src/app/app.module.1.ts'}
<code-example path="server-communication/src/app/app.module.1.ts" linenums="false">
</code-example>
Begin by importing the necessary members.
The newcomers are the `HttpModule` and the `JsonpModule` from the !{_Angular_http_library}. For more information about imports and related terminology, see the [MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) on the `import` statement.
To add these modules to the application, pass them to the `imports` array in the root `@NgModule`.
~~~ {.l-sub-section}
The `HttpModule` is necessary for making HTTP calls.
Though the `JsonpModule` isn't necessary for plain HTTP,
there is a JSONP demo later in this page.
Loading its module now saves time.
~~~
## The Tour of Heroes HTTP client demo
The first demo is a mini-version of the [tutorial](tutorial)'s "Tour of Heroes" (ToH) application.
This version gets some heroes from the server, displays them in a list, lets the user add new heroes, and saves them to the server.
The app uses the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
The app uses the !{_Angular_Http} client to communicate via **XMLHttpRequest (XHR)**.
It works like this:
<figure class='image-display'>
@ -97,14 +132,16 @@ It works like this:
This demo has a single component, the `HeroListComponent`. Here's its template:
{@example 'server-communication/ts/src/app/toh/hero-list.component.html'}
<code-example path="server-communication/src/app/toh/hero-list.component.html">
</code-example>
It presents the list of heroes with an `ngFor`.
Below the list is an input box and an *Add Hero* button where you can enter the names of new heroes
and add them to the database.
A [template reference variable](guide/template-syntax), `newHeroName`, accesses the
value of the input box in the `(click)` event binding.
When the user clicks the button, that value passes to the component's `addHero` method and then
When the user clicks the button, that value is passed to the component's `addHero` method and then
the event binding clears it to make it ready for a new hero name.
Below the button is an area for an error message.
@ -117,7 +154,9 @@ Below the button is an area for an error message.
### The *HeroListComponent* class
Here's the component class:
{@example 'server-communication/ts/src/app/toh/hero-list.component.ts' region='component'}
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component">
</code-example>
Angular [injects](guide/dependency-injection) a `HeroService` into the constructor
and the component calls that service to fetch and save data.
@ -132,9 +171,23 @@ Although _at runtime_ the component requests heroes immediately after creation,
you **don't** call the service's `get` method in the component's constructor.
Instead, call it inside the `ngOnInit` [lifecycle hook](guide/lifecycle-hooks)
and rely on Angular to call `ngOnInit` when it instantiates this component.
~~~ {.l-sub-section}
This is a *best practice*.
Components are easier to test and debug when their constructors are simple, and all real work
(especially calling a remote server) is handled in a separate method.With a basic understanding of the component, you're ready to look inside the `HeroService`.
(especially calling a remote server) is handled in a separate method.
~~~
The service's `getHeroes()` and `create()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server.
Think of an `Observable` as a stream of events published by some source.
To listen for events in this stream, ***subscribe*** to the `Observable`.
These subscriptions specify the actions to take when the web request
produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload).
With a basic understanding of the component, you're ready to look inside the `HeroService`.
{@a HeroService}
@ -144,28 +197,45 @@ Components are easier to test and debug when their constructors are simple, and
In many of the previous samples the app faked the interaction with the server by
returning mock heroes in a service like this one:
{@example 'toh-4/ts/src/app/hero.service.ts' region='just-get-heroes'}
<code-example path="toh-4/src/app/hero.service.ts" region="just-get-heroes" linenums="false">
</code-example>
You can revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='v1'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="v1">
</code-example>
Notice that the !{_Angular_Http} client service is
[injected](guide/dependency-injection) into the `HeroService` constructor.
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='ctor'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor">
</code-example>
Look closely at how to call `!{_priv}http.get`:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='http-get'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="http-get" linenums="false">
</code-example>
You pass the resource URL to `get` and it calls the server which returns heroes.
~~~ {.l-sub-section}
The server returns heroes once you've set up the [in-memory web api](guide/server-communication#in-mem-web-api)
described in the appendix below.
Alternatively, you can temporarily target a JSON file by changing the endpoint URL:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='endpoint-json'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example>
~~~
@ -174,7 +244,9 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U
## Process the response object
Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='extract-data'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false">
</code-example>
The `response` object doesn't hold the data in a form the app can use directly.
You must parse the response data into a JSON object.
@ -182,6 +254,25 @@ You must parse the response data into a JSON object.
{@a parse-to-json}
### Parse to JSON
The response data are in JSON string form.
The app must parse that string into JavaScript objects by calling `response.json()`.
~~~ {.l-sub-section}
This is not Angular's own design.
The Angular HTTP client follows the Fetch specification for the
[response object](https://fetch.spec.whatwg.org/#response-class) returned by the `Fetch` function.
That spec defines a `json()` method that parses the response body into a JavaScript object.
~~~
~~~ {.l-sub-section}
Don't expect the decoded JSON to be the heroes !{_array} directly.
This server always wraps JSON results in an object with a `data`
property. You have to unwrap it to get the heroes.
@ -189,6 +280,10 @@ This is conventional web API behavior, driven by
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
~~~
~~~ {.alert.is-important}
Make no assumptions about the server API.
@ -202,7 +297,7 @@ Not all servers return an object with a `data` property.
{@a no-return-response-object}
### Do not return the response object
The `getHeroes()` method _could_ have returned the HTTP response but this wouldn't
be a best practice.
follow best practices.
The point of a data service is to hide the server interaction details from consumers.
The component that calls the `HeroService` only wants heroes and is kept separate
from getting them, the code dealing with where they come from, and the response object.
@ -219,8 +314,14 @@ but only if it says something that the user can understand and act upon.
This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error.
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='error-handling'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false">
</code-example>
The `catch()` operator passes the error object from `http` to the `handleError()` method.
The `handleError` method transforms the error into a developer-friendly message,
logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`.
{@a subscribe}
@ -233,12 +334,26 @@ This simple app conveys that idea, albeit imperfectly, in the way it handles a `
</h3>
Back in the `HeroListComponent`, in `!{_priv}heroService.getHeroes()`,
the `subscribe` function has a second function parameter to handle the error message.
It sets an `errorMessage` variable that's bound conditionally in the `HeroListComponent` template.
{@example 'server-communication/ts/src/app/toh/hero-list.component.ts' region='getHeroes'}
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false">
</code-example>
~~~ {.l-sub-section}
Want to see it fail? In the `HeroService`, reset the api endpoint to a bad value. Afterward, remember to restore it.
~~~
<a id="create"></a>
<a id="update"></a>
<a id="post"></a>
## Send data to the server
@ -246,11 +361,13 @@ Want to see it fail? In the `HeroService`, reset the api endpoint to a bad value
So far you've seen how to retrieve data from a remote location using an HTTP service.
Now you'll add the ability to create new heroes and save them in the backend.
You'll write a method for the `HeroListComponent` to call, an `addHero()` method, that takes
You'll write a method for the `HeroListComponent` to call, a `create()` method, that takes
just the name of a new hero and returns an `Observable` of `Hero`. It begins like this:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero-sig'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false">
</code-example>
To implement it, you must know the server's API for creating heroes.
@ -269,10 +386,12 @@ The server generates the `id` and returns the entire `JSON` representation
of the new hero including its generated id. The hero arrives tucked inside a response object
with its own `data` property.
Now that you know how the API works, implement `addHero()` as follows:
Now that you know how the API works, implement `create()` as follows:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'}
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
</code-example>
@ -288,8 +407,12 @@ In the `headers` object, the `Content-Type` specifies that the body represents J
As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](guide/server-communication#extract-data)
from the response.
Back in the `HeroListComponent`, its `addHero()` method subscribes to the Observable returned by the service's `create()` method.
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
{@example 'server-communication/ts/src/app/toh/hero-list.component.ts' region='addHero'}
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="addHero" linenums="false">
</code-example>
<h2 id='cors'>
@ -297,19 +420,33 @@ from the response.
</h2>
You just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service.
This is the most common approach for server communication, but it doesn't work in all scenarios.
This is the most common approach to server communication, but it doesn't work in all scenarios.
For security reasons, web browsers block `XHR` calls to a remote server whose origin is different from the origin of the web page.
The *origin* is the combination of URI scheme, hostname, and port number.
This is called the [same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy).
~~~ {.l-sub-section}
Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol.
If the server requires user credentials, enable them in the [request headers](guide/server-communication#headers).
~~~
Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP).
Wikipedia is one such server.
~~~ {.l-sub-section}
This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.
~~~
{@a search-wikipedia}
### Search Wikipedia
@ -321,6 +458,198 @@ types in a text box:
</figure>
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. This example uses the latter.
The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts you to `GET` requests.
All other HTTP methods throw an error because `JSONP` is a read-only facility.
As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts">
</code-example>
The constructor expects Angular to inject its `Jsonp` service, which
is available because `JsonpModule` is in the root `@NgModule` `imports` array
in `app.module.ts`.
<a id="query-parameters"></a>### Search parameters
The [Wikipedia "opensearch" API](https://www.mediawiki.org/wiki/API:Opensearch)
expects four parameters (key/value pairs) to arrive in the request URL's query string.
The keys are `search`, `action`, `format`, and `callback`.
The value of the `search` key is the user-supplied search term to find in Wikipedia.
The other three are the fixed values "opensearch", "json", and "JSONP_CALLBACK" respectively.
~~~ {.l-sub-section}
The `JSONP` technique requires that you pass a callback function name to the server in the query string: `callback=JSONP_CALLBACK`.
The server uses that name to build a JavaScript wrapper function in its response, which Angular ultimately calls to extract the data.
All of this happens under the hood.
~~~
If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this:
<code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false">
</code-example>
In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper:
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false">
</code-example>
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false">
</code-example>
`Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request
to the server.
<a id="wikicomponent"></a>### The WikiComponent
Now that you have a service that can query the Wikipedia API,
turn your attention to the component (template and class) that takes user input and displays search results.
<code-example path="server-communication/src/app/wiki/wiki.component.ts">
</code-example>
The template presents an `<input>` element *search box* to gather search terms from the user,
and calls a `search(term)` method after each `keyup` event.
The component's `search(term)` method delegates to the `WikipediaService`, which returns an
Observable !{_array} of string results (`Observable<string[]>`).
Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`,
the app forwards the Observable result to the template (via `items`) where the `async` pipe
in the `ngFor` handles the subscription. Read more about [async pipes](guide/pipes)
in the [Pipes](guide/pipes) page.
~~~ {.l-sub-section}
The [async pipe](guide/pipes) is a good choice in read-only components
where the component has no need to interact with the data.
`HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list.
~~~
{@a wasteful-app}
## A wasteful app
The Wikipedia search makes too many calls to the server.
It is inefficient and potentially expensive on mobile devices with limited data plans.
### 1. Wait for the user to stop typing
Presently, the code calls the server after every keystroke.
It should only make requests when the user *stops typing*.
Here's how it will work after refactoring:
<figure class='image-display'>
<img src='assets/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"> </img>
</figure>
### 2. Search when the search term changes
Suppose a user enters the word *angular* in the search box and pauses for a while.
The application issues a search request for *angular*.
Then the user backspaces over the last three letters, *lar*, and immediately re-types *lar* before pausing once more.
The search term is still _angular_. The app shouldn't make another request.
### 3. Cope with out-of-order responses
The user enters *angular*, pauses, clears the search box, and enters *http*.
The application issues two search requests, one for *angular* and one for *http*.
Which response arrives first? It's unpredictable.
When there are multiple requests in-flight, the app should present the responses
in the original request order.
In this example, the app must always display the results for the *http* search
no matter which response arrives first.
<a id="more-observables"></a>
## More fun with Observables
You could make changes to the `WikipediaService`, but for a better
user experience, create a copy of the `WikiComponent` instead and make it smarter,
with the help of some nifty Observable operators.
Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`:
<code-tabs>
<code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts">
</code-pane>
<code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts">
</code-pane>
</code-tabs>
While the templates are virtually identical,
there's a lot more RxJS in the "smart" version,
starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators,
imported as [described above](guide/server-communication#rxjs-library).
{@a create-stream}
### Create a stream of search terms
The `WikiComponent` passes a new search term directly to the `WikipediaService` after every keystroke.
The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_
with the help of a `Subject`, which you import from RxJS:
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="import-subject" linenums="false">
</code-example>
The component creates a `searchTermStream` as a `Subject` of type `string`.
The `search()` method adds each new search box value to that stream via the subject's `next()` method.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false">
</code-example>
{@a listen-for-search-terms}
### Listen for search terms
The `WikiSmartComponent` listens to the *stream of search terms* and
processes that stream _before_ calling the service.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="observable-operators" linenums="false">
</code-example>
* <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md" target="_blank" title="debounce operator"><i>debounceTime</i></a>
waits for the user to stop typing for at least 300 milliseconds.
* <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md" target="_blank" title="distinctUntilChanged operator"><i>distinctUntilChanged</i></a>
ensures that the service is called only when the new search term is different from the previous search term.
* The <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md" target="_blank" title="switchMap operator"><i>switchMap</i></a>
calls the `WikipediaService` with a fresh, debounced search term and coordinates the stream(s) of service response.
The role of `switchMap` is particularly important.
The `WikipediaService` returns a separate Observable of string arrays (`Observable<string[]>`) for each search request.
The user could issue multiple requests before a slow server has had time to reply,
which means a backlog of response Observables could arrive at the client, at any moment, in any order.
The `switchMap` returns its own Observable that _combines_ all `WikipediaService` response Observables,
re-arranges them in their original request order,
and delivers to subscribers only the most recent search results.
{@a xsrf}
@ -357,26 +686,39 @@ This sample creates a class that sets the default `Content-Type` header to JSON.
It exports a constant with the necessary `RequestOptions` provider to simplify registration in `AppModule`.
{@example 'server-communication/ts/src/app/default-request-options.service.ts'}
<code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false">
</code-example>
Then it registers the provider in the root `AppModule`.
{@example 'server-communication/ts/src/app/app.module.ts' region='provide-default-request-options'}
<code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false">
</code-example>
Remember to include this provider during setup when unit testing the app's HTTP services.After this change, the `header` option setting in `HeroService.addHero()` is no longer necessary,
~~~ {.l-sub-section}
Remember to include this provider during setup when unit testing the app's HTTP services.
~~~
After this change, the `header` option setting in `HeroService.create()` is no longer necessary,
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'}
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
</code-example>
You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab.
If you're short-circuiting the server call with something like the [_in-memory web api_](guide/server-communication#in-mem-web-api),
try commenting-out the `addHero` header option,
try commenting-out the `create` header option,
set a breakpoint on the POST call, and step through the request processing
to verify the header is there.
Individual requests options, like this one, take precedence over the default `RequestOptions`.
It might be wise to keep the `addHero` request header setting for extra safety.
It might be wise to keep the `create` request header setting for extra safety.
{@a in-mem-web-api}
@ -384,17 +726,29 @@ It might be wise to keep the `addHero` request header setting for extra safety.
## Appendix: Tour of Heroes _in-memory web api_
If the app only needed to retrieve data, you could get the heroes from a `heroes.json` file:
~~~ {.l-sub-section}
You wrap the heroes array in an object with a `data` property for the same reason that a data server does:
to mitigate the [security risk](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk)
posed by top-level JSON arrays.You'd set the endpoint to the JSON file like this:
posed by top-level JSON arrays.
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='endpoint-json'}
~~~
You'd set the endpoint to the JSON file like this:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example>
The *get heroes* scenario would work,
but since the app can't save changes to a JSON file, it needs a web API server.
Because there isn't a real server for this demo,
it substitutes the Angular _in-memory web api_ simulator for the actual XHR backend service.
~~~ {.l-sub-section}
The in-memory web api is not part of Angular _proper_.
It's an optional service in its own
<a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API"><i>angular-in-memory-web-api</i></a>
@ -403,21 +757,60 @@ library installed with npm (see `package.json`).
See the
<a href="https://github.com/angular/in-memory-web-api/blob/master/README.md" target="_blank" title='In-memory Web API "README.md"'><i>README file</i></a>
for configuration options, default behaviors, and limitations.
~~~
The in-memory web API gets its data from !{_a_ca_class_with} a `createDb()`
method that returns a map whose keys are collection names and whose values
are !{_array}s of objects in those collections.
Here's the class for this sample, based on the JSON data:
{@example 'server-communication/ts/src/app/hero-data.ts'}
<code-example path="server-communication/src/app/hero-data.ts" linenums="false">
</code-example>
Ensure that the `HeroService` endpoint refers to the web API:
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='endpoint'}
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint" linenums="false">
</code-example>
Finally, redirect client HTTP requests to the in-memory web API by
adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
At the same time, call its `forRoot()` configuration method with the `HeroData` class.
<code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false">
</code-example>
### How it works
Angular's `http` service delegates the client/server communication tasks
to a helper service called the `XHRBackend`.
Using standard Angular provider registration techniques, the `InMemoryWebApiModule`
replaces the default `XHRBackend` service with its own in-memory alternative.
At the same time, the `forRoot` method initializes the in-memory web API with the *seed data* from the mock hero dataset.
~~~ {.l-sub-section}
The `forRoot()` method name is a strong reminder that you should only call the `InMemoryWebApiModule` _once_,
while setting the metadata for the root `AppModule`. Don't call it again.
~~~
Here is the final, revised version of <span ngio-ex>src/app/app.module.ts</span>, demonstrating these steps.
<code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)">
</code-example>
~~~ {.alert.is-important}
Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that

View File

@ -45,10 +45,17 @@ The HTML `<title>` is in the document `<head>`, outside the body, making it inac
We could grab the browser `document` object and set the title manually.
That's dirty and undermines our chances of running the app outside of a browser someday.
~~~ {.l-sub-section}
Running your app outside a browser means that you can take advantage of server-side
pre-rendering for near-instant first app render times and for SEO. It means you could run from
inside a Web Worker to improve your app's responsiveness by using multiple threads. And it
means that you could run your app inside Electron.js or Windows Universal to deliver it to the desktop.
~~~
## Use the *Title* service
Fortunately, Angular bridges the gap by providing a `Title` service as part of the *Browser platform*.
The [Title](api/platform-browser/index/Title-class) service is a simple class that provides an API
@ -60,7 +67,9 @@ for getting and setting the current HTML document title:
Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
{@example 'cb-set-document-title/ts/src/app/app.component.ts' region='class'}
<code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false">
</code-example>
We bind that method to three anchor tags and, voilà!
<figure class='image-display'>
@ -69,24 +78,25 @@ We bind that method to three anchor tags and, voilà!
Here's the complete solution
<md-tab-group>
<md-tab label="src/main.ts">
{@example 'cb-set-document-title/ts/src/main.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts">
</code-pane>
<md-tab label="src/app/app.module.ts">
{@example 'cb-set-document-title/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts">
</code-pane>
<md-tab label="src/app/app.component.ts">
{@example 'cb-set-document-title/ts/src/app/app.component.ts'}
</md-tab>
<code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
## Why we provide the *Title* service in *bootstrap*

View File

@ -0,0 +1,387 @@
@title
Setup Anatomy
@intro
Inside the local development environment for SystemJS.
@description
The documentation [setup](guide/setup) procedures install a _lot_ of files.
Most of them can be safely ignored.
Application files _inside the_ **`src/`** and **`e2e/`** folders matter most to developers.
Files _outside_ those folders condition the development environment.
They rarely change and you may never view or modify them.
If you do, this page can help you understand their purpose.
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="10%">
</col>
<col width="90%">
</col>
<tr>
<th>
File
</th>
<th>
Purpose
</th>
</tr>
<tr>
<td>
<code>src/app/</code>
</td>
<td>
Angular application files go here.
Ships with the "Hello Angular" sample's
`AppComponent`, `AppModule`, a component unit test (`app.component.spec.ts`), and
the bootstrap file, `main.ts`.
Try the <live-example name="setup">sample application</live-example>
and the <live-example name="setup" plnkr="quickstart-specs">unit test</live-example>
as _live examples_.
</td>
</tr>
<tr>
<td>
<code>e2e/</code>
</td>
<td>
_End-to-end_ (e2e) tests of the application,
written in Jasmine and run by the
<a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a>
e2e test runner.
Initialized with an e2e test for the "Hello Angular" sample.
</td>
</tr>
<tr>
<td>
<code>node_modules/</code>
</td>
<td>
The _npm_ packages installed with the `npm install` command.
</td>
</tr>
<tr>
<td>
<code> .editorconfig<br>
.git/<br>
.gitignore<br>
.travis.yml </code>
</td>
<td>
Tooling configuration files and folders.
Ignore them until you have a compelling reason to do otherwise.
</td>
</tr>
<tr>
<td>
<code>CHANGELOG.md</code>
</td>
<td>
The history of changes to the _QuickStart_ repository.
Delete or ignore.
</td>
</tr>
<tr>
<td>
<code>favicon.ico</code>
</td>
<td>
The application icon that appears in the browser tab.
</td>
</tr>
<tr>
<td>
<code>index.html</code>
</td>
<td>
The application host page.
It loads a few essential scripts in a prescribed order.
Then it boots the application, placing the root `AppComponent`
in the custom `<my-app>` body tag.
The same `index.html` satisfies all documentation application samples.
</td>
</tr>
<tr>
<td>
<code>karma.conf.js</code>
</td>
<td>
Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
test runner described in the [Testing](guide/testing) guide.
</td>
</tr>
<tr>
<td>
<code>karma-test-shim.js</code>
</td>
<td>
Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
with SystemJS as described in the [Testing](guide/testing) guide.
</td>
</tr>
<tr>
<td>
<code>non-essential-files.txt</code>
</td>
<td>
A list of files that you can delete if you want to purge your setup of the
original QuickStart Seed testing and git maintainence artifacts.
See instructions in the optional
[_Deleting non-essential files_](guide/setup) section.
*Do this only in the beginning to avoid accidentally deleting your own tests and git setup!*
</td>
</tr>
<tr>
<td>
<code>LICENSE</code>
</td>
<td>
The open source MIT license to use this setup code in your application.
</td>
</tr>
<tr>
<td>
<code>package.json</code>
</td>
<td>
Identifies `npm `package dependencies for the project.
Contains command scripts for running the application,
running tests, and more. Enter `npm run` for a listing.
<a href="https://github.com/angular/quickstart/blob/master/README.md#npm-scripts"
target="_blank" title="npm scripts for Angular documentation samples">Read more</a> about them.
</td>
</tr>
<tr>
<td>
<code>protractor.config.js</code>
</td>
<td>
Configuration for the
<a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a>
_end-to-end_ (e2e) test runner.
</td>
</tr>
<tr>
<td>
<code>README.md</code>
</td>
<td>
Instruction for using this git repository in your project.
Worth reading before deleting.
</td>
</tr>
<tr>
<td>
<code>styles.css</code>
</td>
<td>
Global styles for the application. Initialized with an `<h1>` style for the QuickStart demo.
</td>
</tr>
<tr>
<td>
<code>systemjs<br>.config.js</code>
</td>
<td>
Tells the **SystemJS** module loader where to find modules
referenced in JavaScript `import` statements. For example:
<code-example language="ts">
import { Component } from '@angular/core;
</code-example>
Don't touch this file unless you are fully versed in SystemJS configuration.
</td>
</tr>
<tr>
<td>
<code>systemjs<br>.config.extras.js</code>
</td>
<td>
Optional extra SystemJS configuration.
A way to add SystemJS mappings, such as for appliation _barrels_,
without changing the original `system.config.js`.
</td>
</tr>
<tr>
<td>
<code>tsconfig.json</code>
</td>
<td>
Tells the TypeScript compiler how to transpile TypeScript source files
into JavaScript files that run in all modern browsers.
</td>
</tr>
<tr>
<td>
<code>tslint.json</code>
</td>
<td>
The `npm` installed TypeScript linter inspects your TypeScript code
and complains when you violate one of its rules.
This file defines linting rules favored by the
[Angular style guide](guide/style-guide) and by the authors of the documentation.
</td>
</tr>
</table>

View File

@ -7,6 +7,7 @@ Install the Angular QuickStart seed for faster, more efficient development on yo
@description
{@a develop-locally}
## Setup a local development environment
@ -111,6 +112,67 @@ Open a terminal window in the project folder and enter the following commands fo
{@a seed}
## What's in the QuickStart seed?
The **QuickStart seed** contains the same application as the QuickStart playground.
But its true purpose is to provide a solid foundation for _local_ development.
Consequently, there are _many more files_ in the project folder on your machine,
most of which you can [learn about later](guide/setup-systemjs-anatomy).
{@a app-files}
Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<aio-filetree>
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
</aio-folder>
<aio-file>
main.ts
</aio-file>
</aio-folder>
</aio-filetree>
<code-tabs>
<code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts">
</code-pane>
<code-pane title="src/main.ts" path="setup/src/main.ts">
</code-pane>
</code-tabs>
All guides and cookbooks have _at least these core files_.
Each file has a distinct purpose and evolves independently as the application grows.
@ -213,11 +275,32 @@ The following are all in `src/`
</table>
~~~ {.l-sub-section}
### Next Step
If you're new to Angular, we recommend staying on the [learning path](guide/learning-angular).
~~~
<br></br><br></br>
{@a install-prerequisites}
## Appendix: !{_prereq}
## Appendix: !{_prereq}
Node.js and npm are essential to modern web development with Angular and other platforms.
Node powers client development and build tools.
The _npm_ package manager, itself a _node_ application, installs JavaScript libraries.
<a href="https://docs.npmjs.com/getting-started/installing-node" target="_blank" title="Installing Node.js and updating npm">
Get them now</a> if they're not already installed on your machine.
**Verify that you are running node `v4.x.x` or higher and npm `3.x.x` or higher**
by running the commands `node -v` and `npm -v` in a terminal/console window.
Older versions produce errors.
We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm.
You may need [nvm](https://github.com/creationix/nvm) if you already have projects running on your machine that
use other versions of node and npm.

View File

@ -6,6 +6,7 @@ Angular has a powerful template engine that lets us easily manipulate the DOM st
@description
<style>
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -46,6 +47,12 @@ The directive then does whatever it's supposed to do with that host element and
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" title="src/app/app.component.html (ngif)" region="ngif">
</code-example>
No brackets. No parentheses. Just `*ngIf` set to a string.
You'll learn in this guide that the [asterisk (*) is a convenience notation](guide/structural-directives#asterisk)
@ -59,6 +66,12 @@ Three of the common, built-in structural directives&mdash;[NgIf](guide/template-
[NgFor](guide/template-syntax), and [NgSwitch...](guide/template-syntax)&mdash;are
described in the [_Template Syntax_](guide/template-syntax) guide and seen in samples throughout the Angular documentation.
Here's an example of them in a template:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in">
</code-example>
This guide won't repeat how to _use_ them. But it does explain _how they work_
and how to [write your own](guide/structural-directives#unless) structural directive.
@ -85,6 +98,9 @@ you apply the directive to an element in the HTML template.
~~~
~~~ {.l-sub-section}
There are two other kinds of Angular directives, described extensively elsewhere:
(1)&nbsp;components and (2)&nbsp;attribute directives.
@ -100,12 +116,22 @@ You can apply many _attribute_ directives to one host element.
You can [only apply one](guide/structural-directives#one-per-element) _structural_ directive to a host element.
~~~
{@a ngIf}
## NgIf case study
`NgIf` is the simplest structural directive and the easiest to understand.
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" title="src/app/app.component.html (ngif-true)" region="ngif-true">
</code-example>
The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM.
Confirm that fact using browser developer tools to inspect the DOM.
@ -124,6 +150,12 @@ The component and DOM nodes can be garbage-collected and free up memory.
### Why *remove* rather than *hide*?
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" title="src/app/app.component.html (display-none)" region="display-none">
</code-example>
While invisible, the element remains in the DOM.
<figure class='image-display'>
@ -162,10 +194,28 @@ Surely you noticed the asterisk (*) prefix to the directive name
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" title="src/app/app.component.html (asterisk)" region="asterisk">
</code-example>
The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular desugars it in two stages.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr">
</code-example>
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template">
</code-example>
* The `*ngIf` directive moved to the `<template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<template>` element.
@ -190,6 +240,12 @@ Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax thro
template _attribute_ to template _element_.
Here's a full-featured application of `NgFor`, written all three ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example>
This is manifestly more complicated than `ngIf` and rightly so.
The `NgFor` directive has more features, both required and optional, than the `NgIf` shown in this guide.
At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`).
@ -293,6 +349,12 @@ One or both elements can be an [`ng-container`](guide/structural-directives#ngco
The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`.
Here's an example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example>
The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
(if any) of the switch cases are displayed.
@ -305,14 +367,33 @@ You attach them to elements using the asterisk (*) prefix notation.
An `NgSwitchCase` displays its host element when its value matches the switch value.
The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
~~~ {.l-sub-section}
The element to which you apply a directive is its _host_ element.
The `<happy-hero>` is the host element for the happy `*ngSwitchCase`.
The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
~~~
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr">
</code-example>
That, in turn, can be desugared into the `<template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
</code-example>
{@a prefer-asterisk}
## Prefer the asterisk (*) syntax.
@ -337,6 +418,12 @@ In fact, before rendering the view, Angular _replaces_ the `<template>` and its
If there is no structural directive and you merely wrap some elements in a `<template>`,
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" title="src/app/app.component.html (template-tag)" region="template-tag">
</code-example>
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
<figure class='image-display'>
@ -356,8 +443,20 @@ as you'll see when you [write your own structural directive](guide/structural-di
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" title="src/app/app.component.html (ngfor-li)" region="ngfor-li">
</code-example>
When there isn't a host element, you can usually wrap the content in a native HTML container element,
such as a `<div>`, and attach the directive to that wrapper.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example>
Introducing another container element&mdash;typically a `<span>` or `<div>`&mdash;to
group the elements under a single _root_ is usually harmless.
_Usually_ ... but not _always_.
@ -365,7 +464,19 @@ _Usually_ ... but not _always_.
The grouping element may break the template appearance because CSS styles
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" title="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" title="src/app/app.component.css (p-span)" region="p-span">
</code-example>
The constructed paragraph renders strangely.
<figure class='image-display'>
@ -379,6 +490,12 @@ For example, the `<select>` element requires `<option>` children.
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" title="src/app/app.component.html (select-span)" region="select-span">
</code-example>
the drop down is empty.
<figure class='image-display'>
@ -393,6 +510,12 @@ The Angular `<ng-container>` is a grouping element that doesn't interfere with s
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" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example>
It renders properly.
<figure class='image-display'>
@ -400,6 +523,12 @@ It renders properly.
</figure>
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example>
The drop down works properly.
<figure class='image-display'>
@ -432,6 +561,12 @@ In this section, you write an `UnlessDirective` structural directive
that does the opposite of `NgIf`.
`NgIf` displays the template content when the condition is `true`.
`UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1">
</code-example>
Creating a directive is similar to creating a component.
* Import the `Directive` decorator (instead of the `Component` decorator).
@ -443,6 +578,12 @@ Creating a directive is similar to creating a component.
* Set the CSS *attribute selector* that identifies the directive when applied to an element in a template.
Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton">
</code-example>
The directive's _selector_ is typically the directive's **attribute name** in square brackets, `[myUnless]`.
The brackets define a CSS
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>.
@ -468,12 +609,31 @@ and access the _view container_ through a
[`ViewContainerRef`](api/core/index/ViewContainerRef-class).
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" title="src/app/unless.directive.ts (ctor)" region="ctor">
</code-example>
### The _myUnless_ property
The directive consumer expects to bind a true/false condition to `[myUnless]`.
That means the directive needs a `myUnless` property, decorated with `@Input`
~~~ {.l-sub-section}
Read about `@Input` in the [_Template Syntax_](guide/template-syntax) guide.
~~~
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (set)" region="set">
</code-example>
Angular sets the `myUnless` property whenever the value of the condition changes.
Because the `myUnless` property does work, it needs a setter.
@ -486,9 +646,21 @@ clear the container which also destroys the view.
Nobody reads the `myUnless` 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" title="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example>
Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleVsAppComp}.
Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless">
</code-example>
When the `condition` is falsy, the top (A) paragraph appears and the bottom (B) paragraph disappears.
When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
@ -506,44 +678,45 @@ You can both try and download the source code for this guide in the <live-exampl
Here is the source from the `src/app/` folder.
<md-tab-group>
<md-tab label="app.component.ts">
{@example 'structural-directives/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts">
</code-pane>
<md-tab label="app.component.html">
{@example 'structural-directives/ts/src/app/app.component.html'}
</md-tab>
<code-pane title="app.component.html" path="structural-directives/src/app/app.component.html">
</code-pane>
<md-tab label="app.component.css">
{@example 'structural-directives/ts/src/app/app.component.css'}
</md-tab>
<code-pane title="app.component.css" path="structural-directives/src/app/app.component.css">
</code-pane>
<md-tab label="app.module.ts">
{@example 'structural-directives/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts">
</code-pane>
<md-tab label="hero.ts">
{@example 'structural-directives/ts/src/app/hero.ts'}
</md-tab>
<code-pane title="hero.ts" path="structural-directives/src/app/hero.ts">
</code-pane>
<md-tab label="hero-switch.components.ts">
{@example 'structural-directives/ts/src/app/hero-switch.components.ts'}
</md-tab>
<code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts">
</code-pane>
<md-tab label="unless.directive.ts">
{@example 'structural-directives/ts/src/app/unless.directive.ts'}
</md-tab>
<code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts">
</code-pane>
</md-tab-group>
</code-tabs>
You learned

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3555
aio/content/guide/testing.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -85,29 +85,30 @@ In _ES5_, you access the Angular entities of the [the Angular packages](glossary
through the global `ng` object.
Anything you can import from `@angular` is a nested member of this `ng` object:
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/app.module.ts' region='ng2import'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/app.module.es6' region='ng2import'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="ng2import">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/app.module.es6' region='ng2import'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/app.module.js' region='ng2import'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import">
</code-pane>
</md-tab-group>
</code-tabs>
### Exporting Application Code
@ -136,29 +137,30 @@ to limit unintentional leaking of private symbols into the global scope.
Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero.component.ts' region='appexport'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6' region='appexport'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="appexport">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero.component.es6' region='appexport'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero.component.js' region='appexport'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport">
</code-pane>
</md-tab-group>
</code-tabs>
### Importing Application Code
@ -166,29 +168,30 @@ In _TypeScript_ and _ES6_ apps, you `import` things that have been exported from
In _ES5_ you use the shared namespace object to access "exported" entities from other files.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/app.module.ts' region='appimport'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/app.module.es6' region='appimport'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="appimport">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/app.module.es6' region='appimport'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/app.module.js' region='appimport'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport">
</code-pane>
</md-tab-group>
</code-tabs>
@ -231,29 +234,30 @@ In _ES6-without-decorators_, properties of classes must be assigned inside the c
_ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero.component.ts' region='class'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6' region='class'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="class">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero.component.es6' region='class'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero.component.js' region='constructorproto'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto">
</code-pane>
</md-tab-group>
</code-tabs>
### Metadata
@ -270,71 +274,66 @@ In _ES5_, you also provide an `annotations` array but you attach it to the _cons
See these variations side-by-side:
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero.component.ts' region='metadata'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6' region='metadata'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="metadata">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero.component.es6' region='metadata'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero.component.js' region='metadata'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata">
</code-pane>
</md-tab-group>
</code-tabs>
***External Template file***
A large component template is often kept in a separate template file.
{@example 'cb-ts-to-js/ts/src/app/hero-title.component.html'}
<code-example path="cb-ts-to-js/ts/src/app/hero-title.component.html" linenums="false">
</code-example>
The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property:
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-title.component.ts' region='templateUrl'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6' region='templateUrl'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6" region="templateUrl">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-title.component.es6' region='templateUrl'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-title.component.js' region='templateUrl'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl">
</code-pane>
</md-tab-group>
</code-tabs>
Note that both the _TypeScript_ and _ES6_ `templateUrl` properties identify the location of the template file _relative to the component module_.
All three metadata configurations specify the `moduleId` property
so that Angular can calculate the proper module address.
The _ES5_ approach shown here does not support modules and therefore there is no way to calculate a _module-relative URL_.
The `templateUrl` for the _ES5_ code must specify the _path from the project root_ and
omits the irrelevant `moduleId` property.
With the right tooling, the `moduleId` may not be needed in the other JavaScript dialects either.
But it's safest to provide it anyway.
{@a dsl}
@ -354,19 +353,20 @@ Then chain a call to the `Class` method which takes an object defining the class
Here is an example of the `HeroComponent`, re-written with the DSL,
next to the original _ES5_ version for comparison:
<md-tab-group>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero.component.js' region='dsl'}
</md-tab>
<code-tabs>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js">
</code-pane>
</md-tab-group>
</code-tabs>
@ -391,7 +391,9 @@ _TypeScript_ and _ES6_ support with getters and setters.
Here's an example of a read-only _TypeScript_ property with a getter
that prepares a toggle-button label for the next clicked state:
{@example 'cb-ts-to-js/ts/src/app/hero-queries.component.ts' region='defined-property'}
<code-example path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="defined-property" linenums="false">
</code-example>
This _TypeScript_ "getter" property is transpiled to an _ES5_
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty"
@ -400,7 +402,9 @@ The _ES5 DSL_ does not support _defined properties_ directly
but you can still create them by extracting the "class" prototype and
adding the _defined property_ in raw JavaScript like this:
{@example 'cb-ts-to-js/js/src/app/hero-queries.component.js' region='defined-property'}
<code-example path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="defined-property" linenums="false">
</code-example>
### DSL for other classes
There are similar DSLs for other decorated classes.
@ -439,34 +443,35 @@ _TypeScript_ interfaces exist for developer convenience and are not used by Angu
They have no physical manifestation in the generated JavaScript code.
Just implement the methods and ignore interfaces when translating code samples from _TypeScript_ to JavaScript.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-lifecycle.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-lifecycle.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
@ -489,34 +494,35 @@ there's nothing fundamentally new about adding another property.
But note that what would have been _separate_ `@Input` and `@Output` property decorators for each class property are
combined in the metadata `inputs` and `outputs` _arrays_.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/confirm.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/confirm.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/confirm.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/confirm.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
In the previous example, one of the public-facing binding names (`cancelMsg`)
differs from the corresponding class property name (`notOkMsg`).
@ -560,34 +566,35 @@ an array whose first parameters are the injectable constructor functions and who
last parameter is the class constructor itself.
This format should be familiar to AngularJS developers.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-di.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-di.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-di.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-di.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
### Injection with the @Inject decorator
@ -607,34 +614,35 @@ Each item constains a new instance of `Inject`:
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-di-inject.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-di-inject.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-di-inject.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
### Additional Injection Decorators
@ -656,42 +664,50 @@ For example, you'd write `new Optional()` in _plain ES6_ and `new ng.core.Option
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
array as before. Use a nested array to define a parameter's complete injection specification.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-title.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-title.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-title.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-title.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
~~~ {.l-sub-section}
In the example above, there is no provider for the `'titlePrefix'` token.
Without `Optional`, Angular would raise an error.
With `Optional`, Angular sets the constructor parameter to `null`
and the component displays the title without a prefix.
~~~
{@a host-binding}
## Host Binding
@ -716,34 +732,35 @@ The `host` value is an object whose properties are host property and listener b
or `(event)` for host listeners.
* Each value identifies the corresponding component property or method.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-host.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-host.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6">
</code-pane>
<md-tab label="ES5 JavaScript">
{@example 'cb-ts-to-js/js/src/app/hero-host.component.js'}
</md-tab>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-host.component.js' region='dsl'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-host.component.js" region="dsl">
</code-pane>
</md-tab-group>
</code-tabs>
### Host Metadata
Some developers prefer to specify host properties and listeners
@ -753,19 +770,20 @@ They'd _rather_ do it the way you _must_ do it _ES5_ and _plain ES6_.
The following re-implementation of the `HeroComponent` reminds us that _any property metadata decorator_
can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_.
These particular _TypeScript_ and _ES6_ code snippets happen to be identical.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-host-meta.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6">
</code-pane>
</md-tab-group>
</code-tabs>
@ -775,10 +793,17 @@ These particular _TypeScript_ and _ES6_ code snippets happen to be identical.
Several _property_ decorators query a component's nested view and content components.
~~~ {.l-sub-section}
_View_ children are associated with element tags that appear _within_ the component's template.
_Content_ children are associated with elements that appear _between_ the component's element tags;
they are projected into an `<ng-content>` slot in the component's template. The [`@ViewChild`](api/core/index/ViewChild-decorator) and
they are projected into an `<ng-content>` slot in the component's template.
~~~
The [`@ViewChild`](api/core/index/ViewChild-decorator) and
[`@ViewChildren`](api/core/index/ViewChildren-decorator) property decorators
allow a component to query instances of other components that are used in
its view.
@ -789,29 +814,30 @@ The `queries` property value is a hash map.
* each _key_ is the name of a component property that will hold the view child or children.
* each _value_ is a new instance of either `ViewChild` or `ViewChildren`.
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-queries.component.ts' region='view'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6' region='view'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="view">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-queries.component.es6' region='view'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-queries.component.js' region='view'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="view">
</code-pane>
</md-tab-group>
</code-tabs>
The [`@ContentChild`](api/core/index/ContentChild-decorator) and
[`@ContentChildren`](api/core/index/ContentChildren-decorator) property decorators
@ -821,29 +847,30 @@ into its view from elsewhere.
They can be added in the same way as [`@ViewChild`](api/core/index/ViewChild-decorator) and
[`@ViewChildren`](api/core/index/ViewChildren-decorator).
<md-tab-group>
<md-tab label="TypeScript">
{@example 'cb-ts-to-js/ts/src/app/hero-queries.component.ts' region='content'}
</md-tab>
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content">
</code-pane>
<md-tab label="ES6 JavaScript with decorators">
{@example 'cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6' region='content'}
</md-tab>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="content">
</code-pane>
<md-tab label="ES6 JavaScript">
{@example 'cb-ts-to-js/js-es6/src/app/hero-queries.component.es6' region='content'}
</md-tab>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content">
</code-pane>
<md-tab label="ES5 JavaScript with DSL">
{@example 'cb-ts-to-js/js/src/app/hero-queries.component.js' region='content'}
</md-tab>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="content">
</code-pane>
</md-tab-group>
</code-tabs>

View File

@ -0,0 +1,119 @@
@title
TypeScript Configuration
@intro
TypeScript configuration for Angular developers.
@description
TypeScript is a primary language for Angular application development.
It is a superset of JavaScript with design-time support for type safety and tooling.
Browsers can't execute TypeScript directly. Typescript must be "transpiled" into JavaScript using the *tsc* compiler,
which requires some configuration.
This page covers some aspects of TypeScript configuration and the TypeScript environment
that are important to Angular developers, including details about the following files:
* [tsconfig.json](guide/typescript-configuration#tsconfig)&mdash;TypeScript compiler configuration.
* [typings](guide/typescript-configuration#typings)&mdash;TypesScript declaration files.
{@a tsconfig}
## *tsconfig.json*
Typically, you add a TypeScript configuration file called `tsconfig.json` to your project to
guide the compiler as it generates JavaScript files.
~~~ {.l-sub-section}
For details about `tsconfig.json`, see the official
[TypeScript wiki](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
~~~
The [Setup](guide/setup) guide uses the following `tsconfig.json`:This file contains options and flags that are essential for Angular applications.
<a id="noImplicitAny"></a>
### *noImplicitAny* and *suppressImplicitAnyIndexErrors*
TypeScript developers disagree about whether the `noImplicitAny` flag should be `true` or `false`.
There is no correct answer and you can change the flag later.
But your choice now can make a difference in larger projects, so it merits discussion.
When the `noImplicitAny` flag is `false` (the default), and if
the compiler cannot infer the variable type based on how it's used,
the compiler silently defaults the type to `any`. That's what is meant by *implicit `any`*.
The documentation setup sets the `noImplicitAny` flag to `true`.
When the `noImplicitAny` flag is `true` and the TypeScript compiler cannot infer
the type, it still generates the JavaScript files, but it also **reports an error**.
Many seasoned developers prefer this stricter setting because type checking catches more
unintentional errors at compile time.
You can set a variable's type to `any` even when the `noImplicitAny` flag is `true`.
When the `noImplicitAny` flag is `true`, you may get *implicit index errors* as well.
Most developers feel that *this particular error* is more annoying than helpful.
You can suppress them with the following additional flag:
<code-example format=".">
"suppressImplicitAnyIndexErrors":true
</code-example>
The documentation setup sets this flag to `true` as well.
{@a typings}
## TypeScript Typings
Many JavaScript libraries, such as jQuery, the Jasmine testing library, and Angular,
extend the JavaScript environment with features and syntax
that the TypeScript compiler doesn't recognize natively.
When the compiler doesn't recognize something, it throws an error.
Use [TypeScript type definition files](https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html)&mdash;`d.ts files`&mdash;to tell the compiler about the libraries you load.
TypeScript-aware editors leverage these same definition files to display type information about library features.
Many libraries include definition files in their npm packages where both the TypeScript compiler and editors
can find them. Angular is one such library.
The `node_modules/@angular/core/` folder of any Angular application contains several `d.ts` files that describe parts of Angular.
**You need do nothing to get *typings* files for library packages that include `d.ts` files.
Angular packages include them already.**
### lib.d.ts
TypeScript includes a special declaration file called `lib.d.ts`. This file contains the ambient declarations for various common JavaScript constructs present in JavaScript runtimes and the DOM.
Based on the `--target`, TypeScript adds _additional_ ambient declarations
like `Promise` if the target is `es6`.
Since the QuickStart is targeting `es5`, you can override the
list of declaration files to be included:
<code-example format=".">
"lib": ["es2015", "dom"]
</code-example>
Thanks to that, you have all the `es6` typings even when targeting `es5`.
### Installable typings files
Many libraries&mdash;jQuery, Jasmine, and Lodash among them&mdash;do *not* include `d.ts` files in their npm packages.
Fortunately, either their authors or community contributors have created separate `d.ts` files for these libraries and
published them in well-known locations.
You can install these typings via `npm` using the
[`@types/*` scoped package](http://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)
and Typescript, starting at 2.0, automatically recognizes them.
For instance, to install typings for `jasmine` you could do `npm install @types/jasmine --save-dev`.
QuickStart identifies two *typings*, or `d.ts`, files:
* [jasmine](http://jasmine.github.io/) typings for the Jasmine test framework.
* [node](https://www.npmjs.com/package/@types/node) for code that references objects in the *nodejs* environment;
you can view an example in the [webpack](guide/webpack) page.
QuickStart doesn't require these typings but many of the samples do.

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,9 @@ 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:
{@example 'user-input/ts/src/app/click-me.component.ts' region='click-me-button'}
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false">
</code-example>
<a id="click"></a>The `(click)` to the left of the equals sign identifies the button's click event as the **target of the binding**.
The text in quotes to the right of the equals sign
@ -36,7 +38,9 @@ usually the Angular component controlling the template.
The example above shows a single line of HTML, but that HTML belongs to a larger component:
{@example 'user-input/ts/src/app/click-me.component.ts' region='click-me-component'}
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false">
</code-example>
When the user clicks the button, Angular calls the `onClickMe` method from `ClickMeComponent`.
@ -46,12 +50,16 @@ 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.
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-1-template'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false">
</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.
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-1-class-no-type'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" linenums="false">
</code-example>
The properties of an `$event` object vary depending on the type of DOM event. For example,
a mouse event includes different information than a input box editing event.
@ -78,6 +86,9 @@ Here's what the UI displays:
</figure>
~~~ {.l-sub-section}
Alternatively, you could accumulate the individual keys themselves by substituting `event.key`
for `event.target.value` in which case the same user input would produce:
<code-example>
@ -87,6 +98,10 @@ for `event.target.value` in which case the same user input would produce:
~~~
{@a keyup1}
### Type the _$event_
@ -97,7 +112,9 @@ that could reveal properties of the event object and prevent silly mistakes.
The following example rewrites the method with types:
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-1-class'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false">
</code-example>
The `$event` is now a specific `KeyboardEvent`.
Not all elements have a `value` property so it casts `target` to an input element.
@ -121,7 +138,9 @@ 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.
{@example 'user-input/ts/src/app/loop-back.component.ts' region='loop-back-component'}
<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" linenums="false">
</code-example>
The template reference variable named `box`, declared on the `<input>` element,
refers to the `<input>` element itself.
@ -138,6 +157,9 @@ Type something in the input box, and watch the display update with each keystrok
</figure>
~~~ {.l-sub-section}
**This won't work at all unless you bind to an event**.
Angular updates the bindings (and therefore the screen)
@ -145,11 +167,17 @@ only if the app does something in response to asynchronous events, such as keyst
This example code binds the `keyup` event
to the number 0, the shortest template statement possible.
While the statement does nothing useful,
it satisfies Angular's requirement so that Angular will update the screen.It's easier to get to the input box with the template reference
it satisfies Angular's requirement so that Angular will update the screen.
~~~
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.
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-2'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" linenums="false">
</code-example>
A nice aspect of this approach is that the component gets clean data values from the view.
It no longer requires knowledge of the `$event` and its structure.
@ -162,7 +190,9 @@ 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_.
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-3'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" linenums="false">
</code-example>
Here's how it works.
<figure class='image-display'>
@ -180,7 +210,9 @@ 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.
{@example 'user-input/ts/src/app/keyup.components.ts' region='key-up-component-4'}
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false">
</code-example>
## Put it all together
@ -199,7 +231,9 @@ clicking **Add**.
Below is the "Little Tour of Heroes" component.
{@example 'user-input/ts/src/app/little-tour.component.ts' region='little-tour'}
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false">
</code-example>
### Observations
@ -219,29 +253,30 @@ clears the input box after a new hero is added to the list.
## Source code
Following is all the code discussed in this page.
<md-tab-group>
<md-tab label="click-me.component.ts">
{@example 'user-input/ts/src/app/click-me.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts">
</code-pane>
<md-tab label="keyup.components.ts">
{@example 'user-input/ts/src/app/keyup.components.ts'}
</md-tab>
<code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts">
</code-pane>
<md-tab label="loop-back.component.ts">
{@example 'user-input/ts/src/app/loop-back.component.ts'}
</md-tab>
<code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts">
</code-pane>
<md-tab label="little-tour.component.ts">
{@example 'user-input/ts/src/app/little-tour.component.ts'}
</md-tab>
<code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts">
</code-pane>
</md-tab-group>
</code-tabs>
## Summary

View File

@ -9,16 +9,30 @@ Use Visual Studio 2015 with the QuickStart files.
This cookbook describes the steps required to set up and use the
Angular QuickStart files in **Visual Studio 2015 within an ASP.NET 4.x project**.
~~~ {.l-sub-section}
There is no *live example* for this cookbook because it describes Visual Studio, not the application.
~~~
<a id="asp-net-4"></a>## ASP.NET 4.x Project
This cookbook explains how to set up the QuickStart files with an **ASP.NET 4.x project** in
Visual Studio 2015.
~~~ {.l-sub-section}
If you prefer a `File | New Project` experience and are using **ASP.NET Core**,
then consider the _experimental_
<a href="http://blog.stevensanderson.com/2016/10/04/angular2-template-for-visual-studio/" target="_blank">ASP.NET Core + Angular template for Visual Studio 2015</a>.
Note that the resulting code does not map to the docs. Adjust accordingly.
~~~
The steps are as follows:
- [Prerequisite](guide/visual-studio-2015#prereq1): Install Node.js
@ -38,11 +52,18 @@ The steps are as follows:
Install **[Node.js® and npm](https://nodejs.org/en/download/)**
if they are not already on your machine.
~~~ {.l-sub-section}
**Verify that you are running node version `4.6.x` or greater, and npm `3.x.x` or greater**
by running `node -v` and `npm -v` in a terminal/console window.
Older versions produce errors.
~~~
<h2 id='prereq2'>
Prerequisite: Visual Studio 2015 Update 3
</h2>
@ -109,10 +130,17 @@ Create the ASP.NET 4.x project in the usual way as follows:
* Select the `ASP.NET Web Application` template, give the project a name, and click OK.
* Select the desired ASP.NET 4.5.2 template and click OK.
~~~ {.l-sub-section}
In this cookbook we'll select the `Empty` template with no added folders,
no authentication and no hosting. Pick the template and options appropriate for your project.
~~~
<h2 id='copy'>
Step 3: Copy the QuickStart files into the ASP.NET project folder
</h2>
@ -154,7 +182,14 @@ First, ensure that `index.html` is set as the start page.
Right-click `index.html` in Solution Explorer and select option `Set As Start Page`.
Build and launch the app with debugger by clicking the **Run** button or press `F5`.
It's faster to run without the debugger by pressing `Ctrl-F5`.The default browser opens and displays the QuickStart sample application.
~~~ {.l-sub-section}
It's faster to run without the debugger by pressing `Ctrl-F5`.
~~~
The default browser opens and displays the QuickStart sample application.
Try editing any of the project files. *Save* and refresh the browser to
see the changes.

View File

@ -13,44 +13,50 @@ Create Angular applications with a Webpack based tooling.
</style>
[**Webpack**](https://webpack.github.io/) is a popular module bundler,
a tool for bundling application source code in convenient _chunks_
a tool for bundling application source code in convenient _chunks_
and for loading that code from a server into a browser.
It's an excellent alternative to the *SystemJS* approach used elsewhere in the documentation.
This guide offers a taste of Webpack and explains how to use it with Angular applications.
<a id="top"></a>
## Table of contents
[What is Webpack?](guide/webpack#what-is-webpack)
# Contents
* [What is Webpack?](guide/webpack#what-is-webpack)
* [Entries and outputs](guide/webpack#entries-outputs)
* [Multiple bundles](guide/webpack#multiple-bundles)
* [Loaders](guide/webpack#loaders)
* [Plugins](guide/webpack#plugins)
[Configuring Webpack](guide/webpack#configure-webpack)
* [Configuring Webpack](guide/webpack#configure-webpack)
* [Polyfills](guide/webpack#polyfills)
* [Common configuration](guide/webpack#common-configuration)
* [Inside `webpack.common.js`](guide/webpack#inside-webpack-commonjs)
- [entry](guide/webpack#common-entries)
- [resolve extension-less imports](guide/webpack#common-resolves)
- [`module.rules`](guide/webpack#common-rules)
- [Plugins](guide/webpack#plugins)
- [`CommonsChunkPlugin`](guide/webpack#commons-chunk-plugin)
- [`HtmlWebpackPlugin`](guide/webpack#html-webpack-plugin)
* [Environment specific configuration](guide/webpack#environment-configuration)
* [Development configuration](guide/webpack#development-configuration)
* [Production configuration](guide/webpack#production-configuration)
* [Test configuration](guide/webpack#test-configuration)
[Trying it out](guide/webpack#try)
[Conclusions](guide/webpack#conclusions)
* [Trying it out](guide/webpack#try)
* [Highlights](guide/webpack#highlights)
* [Conclusion](guide/webpack#conclusion)
You can also <a href="/resources/zips/webpack/webpack.zip">download the final result.</a>
<a id="what-is-webpack"></a>## What is Webpack?
Webpack is a powerful module bundler.
A _bundle_ is a JavaScript file that incorporate _assets_ that *belong* together and
Webpack is a powerful module bundler.
A _bundle_ is a JavaScript file that incorporates _assets_ that *belong* together and
should be served to the client in a response to a single file request.
A bundle can include JavaScript, CSS styles, HTML, and almost any other kind of file.
Webpack roams over your application source code,
looking for `import` statements, building a dependency graph, and emitting one (or more) _bundles_.
With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
Webpack roams over your application source code,
looking for `import` statements, building a dependency graph, and emitting one or more _bundles_.
With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
You determine what Webpack does and how it does it with a JavaScript configuration file, `webpack.config.js`.
@ -59,47 +65,85 @@ You determine what Webpack does and how it does it with a JavaScript configurati
### Entries and outputs
You supply Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries.
The one entry point file in this example is the application's root file, `src/app.ts`:
You supply Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries.
The one entry point file in this example is the application's root file, `src/main.ts`:
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='one-entry'}
<code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false">
</code-example>
Webpack inspects that file and traverses its `import` dependencies recursively.
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='app-example'}
<code-example path="webpack/src/app/app.component.ts" region="component" linenums="false">
It sees that you're importing *@angular/core* so it adds that to its dependency list for (potential) inclusion in the bundle.
It opens the *@angular/core* file and follows _its_ network of `import` statements until it has built the complete dependency graph from `app.ts` down.
</code-example>
It sees that you're importing `@angular/core` so it adds that to its dependency list for potential inclusion in the bundle.
It opens the `@angular/core` file and follows _its_ network of `import` statements until it has built the complete dependency graph from `main.ts` down.
Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration:
<div class='code-example'>
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='one-output'}
<code-example name="webpack.config.js (single output)" language="javascript">
output: {
filename: 'app.js'
}
</code-example>
This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies.
You'll load it later with a `<script>` tag in the `index.html`.
</div>
This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies.
You'll load it later with a `<script>` tag in the `index.html`.
{@a multiple-bundles}
#### Multiple bundles
You probably don't want one giant bundle of everything.
It's preferable to separate the volatile application app code from comparatively stable vendor code modules.
Change the configuration so that it has two entry points, `app.ts` and `vendor.ts`:
Change the configuration so that it has two entry points, `main.ts` and `vendor.ts`:
<div class='code-example'>
<code-example language="javascript">
entry: {
app: 'src/app.ts',
vendor: 'src/vendor.ts'
},
output: {
filename: '[name].js'
}
</code-example>
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='two-entries'}
</div>
Webpack constructs two separate dependency graphs
and emits *two* bundle files, one called `app.js` containing only the application code and
and emits *two* bundle files, one called `app.js` containing only the application code and
another called `vendor.js` with all the vendor dependencies.
~~~ {.l-sub-section}
The `[name]` in the output name is a *placeholder* that a Webpack plugin replaces with the entry names,
`app` and `vendor`. Plugins are [covered later](guide/webpack#commons-chunk-plugin) in the guide.
To tell Webpack what belongs in the vendor bundle,
~~~
To tell Webpack what belongs in the vendor bundle,
add a `vendor.ts` file that only imports the application's third-party modules:
{@example 'webpack/ts/src/vendor.ts'}
<code-example path="webpack/src/vendor.ts" linenums="false">
</code-example>
@ -107,28 +151,54 @@ add a `vendor.ts` file that only imports the application's third-party modules:
### Loaders
Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, html, fonts, whatever.
Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, HTML, fonts, whatever.
Webpack _itself_ only understands JavaScript files.
Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
Configure loaders for TypeScript and CSS as follows.
<div class='code-example'>
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='loaders'}
As Webpack encounters `import` statements like these ...
<code-example language="javascript">
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
},
{
test: /\.css$/,
loaders: 'style-loader!css-loader'
}
]
</code-example>
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='imports'}
</div>
... it applies the `test` RegEx patterns. When a pattern matches the filename, Webpack processes the file with the associated loader.
When Webpack encounters `import` statements like the following,
it applies the `test` RegEx patterns.
<div class='code-example'>
<code-example language="typescript">
import { AppComponent } from './app.component.ts';
import 'uiframework/dist/uiframework.css';
</code-example>
</div>
When a pattern matches the filename, Webpack processes the file with the associated loader.
The first `import` file matches the `.ts` pattern so Webpack processes it with the `awesome-typescript-loader`.
The imported file doesn't match the second pattern so its loader is ignored.
The imported file doesn't match the second pattern so its loader is ignored.
The second `import` matches the second `.css` pattern for which you have *two* loaders chained by the (!) character.
Webpack applies chained loaders *right to left* so it applies
the `css` loader first (to flatten CSS `@import` and `url(...)` statements) and
then the `style` loader (to append the css inside *&lt;style&gt;* elements on the page).
The second `import` matches the second `.css` pattern for which you have *two* loaders chained by the (!) character.
Webpack applies chained loaders *right to left*. So it applies
the `css` loader first to flatten CSS `@import` and `url(...)` statements.
Then it applies the `style` loader to append the css inside `<style>` elements on the page.
{@a plugins}
@ -138,20 +208,29 @@ the `css` loader first (to flatten CSS `@import` and `url(...)` statements) and
Webpack has a build pipeline with well-defined phases.
Tap into that pipeline with plugins such as the `uglify` minification plugin:
<div class='code-example'>
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='plugins'}
<code-example language="javascript">
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
</code-example>
</div>
{@a configure-webpack}
## Configure Webpack
## Configuring Webpack
After that brief orientation, you are ready to build your own Webpack configuration for Angular apps.
After that brief orientation, you are ready to build your own Webpack configuration for Angular apps.
Begin by setting up the development environment.
Create a **new project folder**
Create a new project folder.
<code-example language="sh" class="code-shell">
mkdir angular-webpack
cd angular-webpack
@ -160,43 +239,51 @@ Create a **new project folder**
Add these files:
<md-tab-group>
<md-tab label="package.json">
{@example 'webpack/ts/package.webpack.json'}
</md-tab>
<code-tabs>
<code-pane title="package.json" path="webpack/package.webpack.json">
</code-pane>
<md-tab label="src/tsconfig.json">
{@example 'webpack/ts/src/tsconfig.1.json'}
</md-tab>
<code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json">
</code-pane>
<md-tab label="webpack.config.js">
{@example 'webpack/ts/webpack.config.js'}
</md-tab>
<code-pane title="webpack.config.js" path="webpack/webpack.config.js">
</code-pane>
<md-tab label="karma.conf.js">
{@example 'webpack/ts/karma.webpack.conf.js'}
</md-tab>
<code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js">
</code-pane>
<md-tab label="config/helpers.js">
{@example 'webpack/ts/config/helpers.js'}
</md-tab>
<code-pane title="config/helpers.js" path="webpack/config/helpers.js">
</code-pane>
</md-tab-group>
</code-tabs>
~~~ {.l-sub-section}
Many of these files should be familiar from other Angular documentation guides,
especially the [_Typescript configuration_](guide/typescript-configuration) and
[_npm packages_](guide/npm-packages) guides.
especially the [Typescript configuration](guide/typescript-configuration) and
[npm packages](guide/npm-packages) guides.
Webpack, the plugins, and the loaders are also installed as packages.
Webpack, the plugins, and the loaders are also installed as packages.
They are listed in the updated `packages.json`.
Open a terminal window and (re)install the *npm* packages
~~~
Open a terminal window and install the npm packages.
<code-example language="sh" class="code-shell">
npm install
@ -209,13 +296,15 @@ Open a terminal window and (re)install the *npm* packages
### Polyfills
You'll need polyfills to run an Angular application in most browsers as explained
in the [_Browser Support_](guide/browser-support) guide.
in the [Browser Support](guide/browser-support) guide.
Polyfills should be bundled separately from the application and vendor bundles.
Add a `polyfills.ts` like this one to the `src/` folder.
{@example 'webpack/ts/src/polyfills.ts'}
<code-example path="webpack/src/polyfills.ts" linenums="false">
</code-example>
@ -231,13 +320,13 @@ Load `zone.js` early within `polyfills.ts`, immediately after the other ES6 and
~~~
Because this bundle file will load first, `polyfills.ts` is also a good place to configure the browser environment
Because this bundle file will load first, `polyfills.ts` is also a good place to configure the browser environment
for production or development.
{@a common-configuration}
### Common Configuration
### Common configuration
Developers typically have separate configurations for development, production, and test environments.
All three have a lot of configuration in common.
@ -245,103 +334,153 @@ All three have a lot of configuration in common.
Gather the common configuration in a file called `webpack.common.js`.
{@example 'webpack/ts/config/webpack.common.js'}
<code-example path="webpack/config/webpack.common.js" linenums="false">
</code-example>
{@a inside-webpack-commonjs}
### Inside _webpack.common.js_
Webpack is a NodeJS-based tool that reads configuration from a JavaScript _commonjs_ module file.
Webpack is a NodeJS-based tool that reads configuration from a JavaScript commonjs module file.
The configuration imports dependencies with `require` statements
and exports several objects as properties of a `module.exports` object.
* [`entries`](guide/webpack#common-entries) - the entry-point files that define the bundles.
* [`resolve`](guide/webpack#common-resolve) - how to resolve file names when they lack extensions.
* [`module.rules`](guide/webpack#common-rules) - `module` is an object with `rules` for deciding how files are loaded.
* [`plugins`](guide/webpack#common-plugins) - creates instances of the plugins.
* [`entry`](guide/webpack#common-entries)&mdash;the entry-point files that define the bundles.
* [`resolve`](guide/webpack#common-resolve)&mdash;how to resolve file names when they lack extensions.
* [`module.rules`](guide/webpack#common-rules)&mdash; `module` is an object with `rules` for deciding how files are loaded.
* [`plugins`](guide/webpack#common-plugins)&mdash;creates instances of the plugins.
{@a common-entries}
#### _entries_
#### _entry_
The first export is the *entries* object, described above:
The first export is the `entry` object:
{@example 'webpack/ts/config/webpack.common.js' region='entries'}
<code-example path="webpack/config/webpack.common.js" region="entries" linenums="false">
This *entries* object defines the three bundles:
</code-example>
* polyfills - the polyfills needed to run Angular applications in most modern browsers.
* vendor - the third-party dependencies such as Angular, lodash, and bootstrap.css.
* app - the application code.
This `entry` object defines the three bundles:
* `polyfills`&mdash;the polyfills needed to run Angular applications in most modern browsers.
* `vendor`&mdash;the third-party dependencies such as Angular, lodash, and bootstrap.css.
* `app`&mdash;the application code.
{@a common-resolve}
#### _resolve_ extension-less imports
The app will `import` dozens if not hundreds of JavaScript and TypeScript files.
The app will `import` dozens if not hundreds of JavaScript and TypeScript files.
You could write `import` statements with explicit extensions like this example:
{@example 'webpack/ts-snippets/webpack.config.snippets.ts' region='single-import'}
<div class='code-example'>
<code-example language="typescript">
import { AppComponent } from './app.component.ts';
</code-example>
</div>
But most `import` statements don't mention the extension at all.
Tell Webpack to resolve extension-less file requests by looking for matching files with
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
{@example 'webpack/ts/config/webpack.common.js' region='resolve'}
<code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false">
</code-example>
~~~ {.l-sub-section}
If Webpack should resolve extension-less files for styles and HTML,
add `.css` and `.html` to the list.
~~~
{@a common-rules}
#### _module.rules_
Rules tell Webpack which loaders to use for each file (AKA _module_):
Rules tell Webpack which loaders to use for each file, or module:
{@example 'webpack/ts/config/webpack.common.js' region='loaders'}
<code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false">
</code-example>
* `awesome-typescript-loader`&mdash;a loader to transpile the Typescript code to ES5, guided by the `tsconfig.json` file.
* `angular2-template-loader`&mdash;loads angular components' template and styles.
* `html-loader`&mdash;for component templates.
* images/fonts&mdash;Images and fonts are bundled as well.
* CSS&mdash;the first pattern matches application-wide styles; the second handles
component-scoped styles (the ones specified in a component's `styleUrls` metadata property).
~~~ {.l-sub-section}
* awesome-typescript-loader - a loader to transpile the Typescript code to ES5, guided by the `tsconfig.json` file
* angular2-template-loader - loads angular components' template and styles
* html - for component templates
* images/fonts - Images and fonts are bundled as well.
* css - The pattern matches application-wide styles; the second handles component-scoped styles (the ones specified in a component's `styleUrls` metadata property)
The first pattern is for the application-wide styles. It excludes `.css` files within the `src/app` directory
where the component-scoped styles sit. The `ExtractTextPlugin` (described below) applies the `style` and `css`
loaders to these files.
The second pattern filters for component-scoped styles and loads them as strings via the `raw` loader &mdash;
The second pattern filters for component-scoped styles and loads them as strings via the `raw-loader`,
which is what Angular expects to do with styles specified in a `styleUrls` metadata property.
~~~
~~~ {.l-sub-section}
Multiple loaders can be chained using the array notation.
~~~
{@a common-plugins}
#### _plugins_
Finally, create instances of three plugins:
{@example 'webpack/ts/config/webpack.common.js' region='plugins'}
<code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false">
</code-example>
{@a commons-chunk-plugin}
#### *CommonsChunkPlugin*
The `app.js` bundle should contain only application code. All vendor code belongs in the `vendor.js` bundle.
The `app.js` bundle should contain only application code. All vendor code belongs in the `vendor.js` bundle.
Of course the application code `imports` vendor code.
Webpack itself is not smart enough to keep the vendor code out of the `app.js` bundle.
The `CommonsChunkPlugin` does that job.
The `CommonsChunkPlugin` identifies the hierarchy among three _chunks_: `app` -> `vendor` -> `polyfills`.
Of course the application code imports vendor code.
On its own, Webpack is not smart enough to keep the vendor code out of the `app.js` bundle.
The `CommonsChunkPlugin` does that job.
~~~ {.l-sub-section}
The `CommonsChunkPlugin` identifies the hierarchy among three _chunks_: `app` -> `vendor` -> `polyfills`.
Where Webpack finds that `app` has shared dependencies with `vendor`, it removes them from `app`.
It would remove `polyfills` from `vendor` if they shared dependencies (which they don't).
It would remove `polyfills` from `vendor` if they shared dependencies, which they don't.
~~~
{@a html-webpack-plugin}
#### *HtmlWebpackPlugin*
#### _HtmlWebpackPlugin_
Webpack generates a number of js and css files.
Webpack generates a number of js and CSS files.
You _could_ insert them into the `index.html` _manually_. That would be tedious and error-prone.
Webpack can inject those scripts and links for you with the `HtmlWebpackPlugin`.
@ -350,7 +489,7 @@ Webpack can inject those scripts and links for you with the `HtmlWebpackPlugin`.
### Environment-specific configuration
The `webpack.common.js` configuration file does most of the heavy lifting.
The `webpack.common.js` configuration file does most of the heavy lifting.
Create separate, environment-specific configuration files that build on `webpack.common`
by merging into it the peculiarities particular to the target environments.
@ -359,27 +498,30 @@ These files tend to be short and simple.
{@a development-configuration}
### Development Configuration
### Development configuration
Here is the `webpack.dev.js` development configuration file.
Here is the `webpack.dev.js` development configuration file.
{@example 'webpack/ts/config/webpack.dev.js'}
<code-example path="webpack/config/webpack.dev.js" linenums="false">
</code-example>
The development build relies on the Webpack development server, configured near the bottom of the file.
Although you tell Webpack to put output bundles in the `dist` folder,
the dev server keeps all bundles in memory; it doesn't write them to disk.
You won't find any files in the `dist` folder (at least not any generated from *this development build*).
You won't find any files in the `dist` folder, at least not any generated from *this development build*.
The `HtmlWebpackPlugin` (added in `webpack.common.js`) use the *publicPath* and the *filename* settings to generate
appropriate &lt;script&gt; and &lt;link&gt; tags into the `index.html`.
The `HtmlWebpackPlugin`, added in `webpack.common.js`, uses the `publicPath` and the `filename` settings to generate
appropriate `<script>` and `<link>` tags into the `index.html`.
The CSS styles are buried inside the Javascript bundles by default. The `ExtractTextPlugin` extracts them into
external `.css` files that the `HtmlWebpackPlugin` inscribes as &lt;link&gt; tags into the `index.html`.
external `.css` files that the `HtmlWebpackPlugin` inscribes as `<link>` tags into the `index.html`.
Refer to the Webpack documentation for details on these and other configuration options in this file
Refer to the [Webpack documentation](https://webpack.github.io/docs/) for details on these and
other configuration options in this file.
Grab the app code at the end of this guide and try:
@ -392,12 +534,14 @@ Grab the app code at the end of this guide and try:
{@a production-configuration}
### Production Configuration
### Production configuration
Configuration of a *production* build resembles *development* configuration ... with a few key changes.
Configuration of a *production* build resembles *development* configuration with a few key changes.
{@example 'webpack/ts/config/webpack.prod.js'}
<code-example path="webpack/config/webpack.prod.js" linenums="false">
</code-example>
You'll deploy the application and its dependencies to a real production server.
You won't deploy the artifacts needed only in development.
@ -405,20 +549,22 @@ You won't deploy the artifacts needed only in development.
Put the production output bundle files in the `dist` folder.
Webpack generates file names with cache-busting hash.
Thanks to the `HtmlWebpackPlugin`, you don't have to update the `index.html` file when the hashes changes.
Thanks to the `HtmlWebpackPlugin`, you don't have to update the `index.html` file when the hash changes.
There are additional plugins:
* **NoEmitOnErrorsPlugin** - stops the build if there is an error.
* **UglifyJsPlugin** - minifies the bundles.
* **ExtractTextPlugin** - extracts embedded css as external files, adding cache-busting hash to the filename.
* **DefinePlugin** - use to define environment variables that you can reference within the application.
* **LoaderOptionsPlugins** - to override options of certain loaders.
* *`NoEmitOnErrorsPlugin`&mdash;stops the build if there is an error.
* *`UglifyJsPlugin`&mdash;minifies the bundles.
* *`ExtractTextPlugin`&mdash;extracts embedded css as external files, adding cache-busting hash to the filename.
* *`DefinePlugin`&mdash;use to define environment variables that you can reference within the application.
* *`LoaderOptionsPlugins`&mdash;to override options of certain loaders.
Thanks to the *DefinePlugin* and the `ENV` variable defined at top, you can enable Angular production mode like this:
Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this:
{@example 'webpack/ts/src/main.ts' region='enable-prod'}
<code-example path="webpack/src/main.ts" region="enable-prod" linenums="false">
</code-example>
Grab the app code at the end of this guide and try:
@ -431,9 +577,9 @@ Grab the app code at the end of this guide and try:
{@a test-configuration}
### Test Configuration
### Test configuration
You don't need much configuration to run unit tests.
You don't need much configuration to run unit tests.
You don't need the loaders and plugins that you declared for your development and production builds.
You probably don't need to load and process the application-wide styles files for unit tests and doing so would slow you down;
you'll use the `null` loader for those CSS files.
@ -442,25 +588,31 @@ You could merge the test configuration into the `webpack.common` configuration a
But it might be simpler to start over with a completely fresh configuration.
{@example 'webpack/ts/config/webpack.test.js'}
<code-example path="webpack/config/webpack.test.js" linenums="false">
Reconfigure karma to use webpack to run the tests:
</code-example>
Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests:
{@example 'webpack/ts/config/karma.conf.js'}
<code-example path="webpack/config/karma.conf.js" linenums="false">
</code-example>
You don't precompile the TypeScript; Webpack transpiles the Typescript files on the fly, in memory, and feeds the emitted JS directly to Karma.
There are no temporary files on disk.
The `karma-test-shim` tells Karma what files to pre-load and
The `karma-test-shim` tells Karma what files to pre-load and
primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded.
{@example 'webpack/ts/config/karma-test-shim.js'}
<code-example path="webpack/config/karma-test-shim.js" linenums="false">
</code-example>
Notice that you do _not_ load the application code explicitly.
You tell Webpack to find and load the test files (the files ending in `.spec.ts`).
Each spec file imports all &mdash; and only &mdash; the application source code that it tests.
Each spec file imports all&mdash;and only&mdash;the application source code that it tests.
Webpack loads just _those_ specific application files and ignores the other files that you aren't testing.
Grab the app code at the end of this guide and try:
@ -474,95 +626,99 @@ Grab the app code at the end of this guide and try:
Here is the source code for a small application that bundles with the
Webpack techniques covered in this guide.
<md-tab-group>
<md-tab label="src/index.html">
{@example 'webpack/ts/src/index.html'}
</md-tab>
<code-tabs>
<code-pane title="src/index.html" path="webpack/src/index.html">
</code-pane>
<md-tab label="src/main.ts">
{@example 'webpack/ts/src/main.ts'}
</md-tab>
<code-pane title="src/main.ts" path="webpack/src/main.ts">
</code-pane>
<md-tab label="src/assets/css/styles.css">
{@example 'webpack/ts/src/assets/css/styles.css'}
</md-tab>
<code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css">
</code-pane>
</md-tab-group>
</code-tabs>
<md-tab-group>
<md-tab label="src/app/app.component.ts">
{@example 'webpack/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts">
</code-pane>
<md-tab label="src/app/app.component.html">
{@example 'webpack/ts/src/app/app.component.html'}
</md-tab>
<code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html">
</code-pane>
<md-tab label="src/app/app.component.css">
{@example 'webpack/ts/src/app/app.component.css'}
</md-tab>
<code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css">
</code-pane>
<md-tab label="src/app/app.component.spec.ts">
{@example 'webpack/ts/src/app/app.component.spec.ts'}
</md-tab>
<code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts">
</code-pane>
<md-tab label="src/app/app.module.ts">
{@example 'webpack/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts">
</code-pane>
</md-tab-group>
</code-tabs>
The <code>app.component.html</code> displays this downloadable Angular logo
<a href="https://raw.githubusercontent.com/angular/angular.io/master/publicassets/images/logos/angular2/angular.png" target="_blank">
<img src="assets/images/logos/angular2/angular.png" height="40px" title="download Angular logo"></a>.
Create folder `images` under the project's "assets" folder, then right-click and download the image to that folder.
Create a folder called `images` under the project's `assets` folder, then right-click (Cmd+click on Mac)
on the image and download it to that folder.
{@a bundle-ts}
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
<md-tab-group>
<md-tab label="src/polyfills.ts">
{@example 'webpack/ts/src/polyfills.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts">
</code-pane>
<md-tab label="src/vendor.ts">
{@example 'webpack/ts/src/vendor.ts'}
</md-tab>
<code-pane title="src/vendor.ts" path="webpack/src/vendor.ts">
</code-pane>
</md-tab-group>
</code-tabs>
<a id="highlights"></a>### Highlights:
<a id="highlights"></a>### Highlights
* There are no &lt;script&gt; or &lt;link&gt; tags in the `index.html`.
* There are no `<script>` or `<link>` tags in the `index.html`.
The `HtmlWebpackPlugin` inserts them dynamically at runtime.
* The `AppComponent` in `app.component.ts` imports the application-wide css with a simple `import` statement.
* The `AppComponent` itself has its own html template and css file. WebPack loads them with calls to `require()`.
* The `AppComponent` itself has its own html template and css file. WebPack loads them with calls to `require()`.
Webpack stashes those component-scoped files in the `app.js` bundle too.
You don't see those calls in the source code;
they're added behind the scenes by the `angular2-template-loader` plug-in.
You don't see those calls in the source code;
they're added behind the scenes by the `angular2-template-loader` plug-in.
* The `vendor.ts` consists of vendor dependency `import` statements that drive the `vendor.js` bundle.
The application imports these modules too; they'd be duplicated in the `app.js` bundle
if the `CommonsChunkPlugin` hadn't detected the overlap and removed them from `app.js`.
<a id="conclusions"></a>## Conclusions
<a id="conclusion"></a>## Conclusion
You've learned just enough Webpack to configurate development, test and production builds
You've learned just enough Webpack to configurate development, test and production builds
for a small Angular application.
_You could always do more_. Search the web for expert advice and expand your Webpack knowledge.