docs(aio): fix paths to images
This commit is contained in:
parent
0d63e2a586
commit
b70c881c00
|
@ -2,7 +2,7 @@
|
|||
AngularJS to Angular Quick Reference
|
||||
|
||||
@intro
|
||||
Learn how AngularJS concepts and techniques map to Angular
|
||||
Learn how AngularJS concepts and techniques map to Angular.
|
||||
|
||||
@description
|
||||
|
||||
|
@ -16,16 +16,16 @@ by mapping AngularJS syntax to the equivalent Angular syntax.
|
|||
**See the Angular syntax in this <live-example name="cb-ajs-quick-reference"></live-example>**.
|
||||
|
||||
## Contents
|
||||
This page covers:
|
||||
* [Template basics](#template-basics) - binding and local variables.
|
||||
|
||||
* [Template directives](#template-directives) - built-in directives `ngIf` and `ngClass`.
|
||||
* [Template basics](#template-basics)—binding and local variables.
|
||||
|
||||
* [Filters/pipes](#filters-pipes) - built-in *filters*, known as *pipes* in Angular.
|
||||
* [Template directives](#template-directives)—built-in directives `ngIf` and `ngClass`.
|
||||
|
||||
* [Modules/controllers/components](#controllers-components) - *modules* in Angular are slightly different from *modules* in AngularJS, and *controllers* are *components* in Angular.
|
||||
* [Filters/pipes](#filters-pipes)—built-in *filters*, known as *pipes* in Angular.
|
||||
|
||||
* [Style sheets](#style-sheets) - more options for CSS than in AngularJS.
|
||||
* [Modules/controllers/components](#controllers-components)—*modules* in Angular are slightly different from *modules* in AngularJS, and *controllers* are *components* in Angular.
|
||||
|
||||
* [Style sheets](#style-sheets)—more options for CSS than in AngularJS.
|
||||
|
||||
## Template basics
|
||||
Templates are the user-facing part of an Angular application and are written in HTML.
|
||||
|
@ -86,7 +86,8 @@ The following table lists some of the key AngularJS template features with their
|
|||
The context of the binding is implied and is always the
|
||||
associated component, so it needs no reference variable.
|
||||
|
||||
For more information, see the [Interpolation](../guide/template-syntax.html#interpolation) section of the Template Syntax page.
|
||||
For more information, see the [Interpolation](../guide/template-syntax.html#interpolation)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -116,7 +117,7 @@ The following table lists some of the key AngularJS template features with their
|
|||
Many (but not all) of the built-in filters from AngularJS are
|
||||
built-in pipes in Angular.
|
||||
|
||||
For more information, see the heading [Filters/pipes](#filters-pipes) below.
|
||||
For more information, see [Filters/pipes](#filters-pipes) below.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -144,7 +145,8 @@ The following table lists some of the key AngularJS template features with their
|
|||
|
||||
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.html#ngForMicrosyntax) section of the Template Syntax page.
|
||||
For more information, see the [ngFor micro-syntax](../guide/template-syntax.html#microsyntax)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -260,7 +262,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
Angular also has **class binding**, which is a good way to add or remove a single class,
|
||||
as shown in the third example.
|
||||
|
||||
For more information see the [Attribute, Class, and Style Bindings](../guide/template-syntax.html#other-bindings) section of the Template Syntax page.
|
||||
For more information see the [Attribute, class, and style bindings](../guide/template-syntax.html#other-bindings)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -287,7 +290,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
|
||||
<td>
|
||||
### bind to the `click` event
|
||||
### Bind to the `click` event
|
||||
|
||||
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='event-binding'}
|
||||
|
||||
|
@ -306,7 +309,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
For a list of DOM events, see: https://developer.mozilla.org/en-US/docs/Web/Events.
|
||||
|
||||
For more information, see the [Event Binding](../guide/template-syntax.html#event-binding) section of the Template Syntax page.
|
||||
For more information, see the [Event binding](../guide/template-syntax.html#event-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -354,7 +358,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
|
||||
<td>
|
||||
### bind to the `hidden` property
|
||||
### Bind to the `hidden` property
|
||||
In Angular, you use property binding; there is no built-in *hide* directive.
|
||||
For more information, see [ng-show](#ng-show).
|
||||
</td>
|
||||
|
@ -385,20 +389,22 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
|
||||
<td>
|
||||
### bind to the `href` property
|
||||
### 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.
|
||||
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 on property binding, see [Template Syntax](../guide/template-syntax.html#property-binding).
|
||||
For more information see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
|
||||
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the third example.
|
||||
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 [Routing & Navigation](../guide/router.html#router-link).
|
||||
For more information on routing, see the [RouterLink binding](../guide/router.html#router-link)
|
||||
section of the [Routing & Navigation](../guide/router.html) page.
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -417,7 +423,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
In AngularJS, the `ng-if` directive removes or recreates a portion of the DOM,
|
||||
based on an expression. If the expression is false, the element is removed from the DOM.
|
||||
|
||||
In this example, the `table` element is removed from the DOM unless the `movies` array has a length greater than zero.
|
||||
In this example, the `<table>` element is removed from the DOM unless the `movies` array has a length greater than zero.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -426,9 +432,10 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
{@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 or recreates a portion of the DOM based on an expression.
|
||||
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.
|
||||
In this example, the `<table>` element is removed from the DOM unless the `movies` array has a length.
|
||||
|
||||
The (*) before `ngIf` is required in this example.
|
||||
For more information, see [Structural Directives](../guide/structural-directives.html).
|
||||
|
@ -459,7 +466,9 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
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 [Template Syntax](../guide/template-syntax.html#ngModel).
|
||||
For more information on two-way binding with `ngModel`, see the [NgModel—Two-way binding to
|
||||
form elements with `[(ngModel)]`](../guide/template-syntax.html#ngModel)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -477,7 +486,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
In AngularJS, the `ng-repeat` directive repeats the associated DOM element
|
||||
for each item in the specified collection.
|
||||
|
||||
In this example, the table row (`tr`) element repeats for each movie object in the collection of movies.
|
||||
In this example, the table row (`<tr>`) element repeats for each movie object in the collection of movies.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -486,8 +495,9 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
{@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 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
|
||||
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.
|
||||
|
||||
Notice the other syntax differences:
|
||||
|
@ -515,24 +525,25 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
In AngularJS, the `ng-show` directive shows or hides the associated DOM element, based on
|
||||
an expression.
|
||||
|
||||
In this example, the `div` element is shown if the `favoriteHero` variable is truthy.
|
||||
In this example, the `<div>` element is shown if the `favoriteHero` variable is truthy.
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
### bind to the `hidden` property
|
||||
### 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.
|
||||
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
|
||||
set it to a quoted template expression that evaluates to the *opposite* of *show*.
|
||||
|
||||
In this example, the `div` element is hidden if the `favoriteHero` variable is not truthy.
|
||||
In this example, the `<div>` element is hidden if the `favoriteHero` variable is not truthy.
|
||||
|
||||
For more information on property binding, see [Template Syntax](../guide/template-syntax.html#property-binding).
|
||||
For more information on property binding, see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -554,14 +565,15 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
|
||||
<td>
|
||||
### bind to the `src` property
|
||||
### 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.
|
||||
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 [Template Syntax](../guide/template-syntax.html#property-binding).
|
||||
For more information on property binding, see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -578,7 +590,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
In AngularJS, the `ng-style` directive sets a CSS style on an HTML element
|
||||
based on an expression. That expression is often a key-value control object with each
|
||||
key of the object defined as a CSS style name, and each value defined as an expression
|
||||
key of the object defined as a CSS property, and each value defined as an expression
|
||||
that evaluates to a value appropriate for the style.
|
||||
|
||||
In the example, the `color` style is set to the current value of the `colorPreference` variable.
|
||||
|
@ -596,9 +608,11 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
Angular also has **style binding**, which is good way to set a single style. This is shown in the second example.
|
||||
|
||||
For more information on style binding, see [Template Syntax](../guide/template-syntax.html#style-binding).
|
||||
For more information on style binding, see the [Style binding](../guide/template-syntax.html#style-binding) section of the
|
||||
[Template Syntax](../guide/template-syntax.html) page.
|
||||
|
||||
For more information on the ngStyle directive, see [Template Syntax](../guide/template-syntax.html#ngStyle).
|
||||
For more information on the `ngStyle` directive, see [NgStyle](../guide/template-syntax.html#ngStyle)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -650,7 +664,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
|
||||
The (*) before `ngSwitchCase` and `ngSwitchDefault` is required in this example.
|
||||
|
||||
For more information on the ngSwitch directive, see [Template Syntax](../guide/template-syntax.html#ngSwitch).
|
||||
For more information, see [The NgSwitch directives](../guide/template-syntax.html#ngSwitch)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -665,7 +680,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
|||
{@a filters-pipes}
|
||||
|
||||
## Filters/pipes
|
||||
Angular **pipes** provide formatting and transformation for data in our template, similar to AngularJS **filters**.
|
||||
Angular **pipes** provide formatting and transformation for data in the template, similar to AngularJS **filters**.
|
||||
Many of the built-in filters in AngularJS have corresponding pipes in Angular.
|
||||
For more information on pipes, see [Pipes](../guide/pipes.html).
|
||||
|
||||
|
@ -704,7 +719,7 @@ For more information on pipes, see [Pipes](../guide/pipes.html).
|
|||
<td>{{movie.price | currency}}</td>
|
||||
</code-example>
|
||||
|
||||
Formats a number as a currency.
|
||||
Formats a number as currency.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -949,17 +964,19 @@ The Angular code is shown using TypeScript.
|
|||
}());
|
||||
</code-example>
|
||||
|
||||
In AngularJS, you often defined an immediately invoked function expression (or IIFE) around your controller code.
|
||||
This kept your controller code out of the global namespace.
|
||||
In AngularJS, an immediately invoked function expression (or IIFE) around controller code
|
||||
keeps it out of the global namespace.
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
### none
|
||||
You don't need to worry about this in Angular because you use ES 2015 modules
|
||||
and modules handle the namespacing for you.
|
||||
This is a nonissue in Angular because ES 2015 modules
|
||||
handle the namespacing for you.
|
||||
|
||||
For more information on modules, see [Architecture Overview](../guide/architecture.html#module).
|
||||
For more information on modules, see the [Modules](../guide/architecture.html#modules) section of the
|
||||
[Architecture Overview](../guide/architecture.html).
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -974,7 +991,8 @@ The Angular code is shown using TypeScript.
|
|||
angular.module("movieHunter", ["ngRoute"]);
|
||||
</code-example>
|
||||
|
||||
In AngularJS, an Angular module keeps track of controllers, services, and other code. The second argument defines the list of other modules that this module depends upon.
|
||||
In AngularJS, an Angular module keeps track of controllers, services, and other code.
|
||||
The second argument defines the list of other modules that this module depends upon.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -987,7 +1005,7 @@ The Angular code is shown using TypeScript.
|
|||
- `imports`: specifies the list of other modules that this module depends upon
|
||||
- `declaration`: keeps track of your components, pipes, and directives.
|
||||
|
||||
For more information on modules, see [Angular Modules](../guide/ngmodule.html).
|
||||
For more information on modules, see [Angular Modules (NgModule)](../guide/ngmodule.html).
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -1006,7 +1024,7 @@ The Angular code is shown using TypeScript.
|
|||
MovieListCtrl]);
|
||||
</code-example>
|
||||
|
||||
AngularJS, has code in each controller that looks up an appropriate Angular module
|
||||
AngularJS has code in each controller that looks up an appropriate Angular module
|
||||
and registers the controller with that module.
|
||||
|
||||
The first argument is the controller name. The second argument defines the string names of
|
||||
|
@ -1015,17 +1033,18 @@ The Angular code is shown using TypeScript.
|
|||
|
||||
|
||||
<td>
|
||||
### Component Decorator
|
||||
### 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.
|
||||
The Component decorator declares that the class is a component and provides metadata about
|
||||
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.
|
||||
|
||||
This is how you associate a template with code, which is defined in the component class.
|
||||
This is how you associate a template with logic, which is defined in the component class.
|
||||
|
||||
For more information, see the [Components](../guide/architecture.html#components) section of the Architecture Overview page.
|
||||
For more information, see the [Components](../guide/architecture.html#components)
|
||||
section of the [Architecture Overview](../guide/architecture.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -1054,7 +1073,8 @@ The Angular code is shown using TypeScript.
|
|||
|
||||
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
|
||||
|
||||
For more information, see the [Components](../guide/architecture.html#components) section of the Architecture Overview page.
|
||||
For more information, see the [Components](../guide/architecture.html#components)
|
||||
section of the [Architecture Overview](../guide/architecture.html) page.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -1088,7 +1108,8 @@ The Angular code is shown using TypeScript.
|
|||
This example injects a `MovieService`.
|
||||
The first parameter's TypeScript type tells Angular what to inject, even after minification.
|
||||
|
||||
For more information, see the [Dependency Injection](../guide/architecture.html#dependency-injection) section of the Architecture Overview.
|
||||
For more information, see the [Dependency injection](../guide/architecture.html#dependency-injection)
|
||||
section of the [Architecture Overview](../guide/architecture.html).
|
||||
</td>
|
||||
|
||||
|
||||
|
|
|
@ -615,9 +615,9 @@ The `source-map-explorer` analyzes the source map generated with the bundle and
|
|||
showing exactly which application and Angular modules and classes are included in the bundle.
|
||||
|
||||
Here's the map for _Tour of Heroes_.
|
||||
<a href="/resources/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image">
|
||||
<a href="assets/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image">
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img>
|
||||
<img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img>
|
||||
</figure>
|
||||
|
||||
</a>
|
|
@ -44,7 +44,7 @@ and each iteration's `hero` instance to the child's `hero` property.
|
|||
The running application displays three heroes:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child"> </img>
|
||||
</figure>
|
||||
|
||||
### Test it
|
||||
|
@ -73,7 +73,7 @@ Here's the `NameParentComponent` demonstrating name variations including a name
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter"> </img>
|
||||
</figure>
|
||||
|
||||
### Test it
|
||||
|
@ -103,7 +103,7 @@ The `VersionParentComponent` supplies the `minor` and `major` values and binds b
|
|||
Here's the output of a button-pushing sequence:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"> </img>
|
||||
</figure>
|
||||
|
||||
### Test it
|
||||
|
@ -140,7 +140,7 @@ The framework passes the event argument — represented by `$event` —
|
|||
and the method processes it:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent"> </img>
|
||||
</figure>
|
||||
|
||||
### Test it
|
||||
|
@ -185,7 +185,7 @@ use interpolation to display the child's `seconds` property.
|
|||
Here we see the parent and child working together.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -287,7 +287,7 @@ the parent `MissionControlComponent` and the `AstronautComponent` children,
|
|||
facilitated by the service:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service"> </img>
|
||||
<img src="assets/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service"> </img>
|
||||
</figure>
|
||||
|
||||
### Test it
|
||||
|
|
|
@ -122,7 +122,7 @@ The author simply declared what was needed in the constructor (`LoggerService` a
|
|||
Once all the dependencies are in place, the `AppComponent` displays the user information:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User"> </img>
|
||||
</figure>
|
||||
|
||||
### *@Injectable()*
|
||||
|
@ -233,7 +233,7 @@ And the template displays this data-bound property.
|
|||
Find this example in <live-example name="cb-dependency-injection">live code</live-example>
|
||||
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -280,7 +280,7 @@ placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
|
|||
|
||||
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact"> </img>
|
||||
</figure>
|
||||
|
||||
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
|
||||
|
@ -304,14 +304,14 @@ Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest o
|
|||
|
||||
We'll come back to the `elementRef` property shortly.Here's the `HeroBiosAndContactsComponent` in action.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img>
|
||||
</figure>
|
||||
|
||||
If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree
|
||||
until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates
|
||||
with the gratuitous "!!!", indicating that the logger was found.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"> </img>
|
||||
</figure>
|
||||
|
||||
On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`,
|
||||
|
@ -343,7 +343,7 @@ first without a value (yielding the default color) and then with an assigned col
|
|||
|
||||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios"> </img>
|
||||
</figure>
|
||||
|
||||
<a id="providers"></a>
|
||||
|
@ -394,7 +394,7 @@ We need other ways to deliver dependency values and that means we need other way
|
|||
The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px"> </img>
|
||||
</figure>
|
||||
|
||||
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
|
||||
|
@ -493,14 +493,14 @@ We want to shrink that API surface to just the two members exposed by the `Minim
|
|||
|
||||
The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"> </img>
|
||||
</figure>
|
||||
|
||||
Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService`
|
||||
which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`.
|
||||
The following image, which displays the logging date, confirms the point:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -641,7 +641,7 @@ In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComp
|
|||
to display a *sorted* list of heroes.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/sorted-heroes.png" alt="Sorted Heroes"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/sorted-heroes.png" alt="Sorted Heroes"> </img>
|
||||
</figure>
|
||||
|
||||
The `HeroesBaseComponent` could stand on its own.
|
||||
|
@ -783,7 +783,7 @@ The [*forwardRef*](#forwardref) breaks the circular reference we just created by
|
|||
|
||||
Here's *Alex* and family in action:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/alex.png" alt="Alex in action"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/alex.png" alt="Alex in action"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -834,7 +834,7 @@ which *is* what parent means.
|
|||
Here's *Alice*, *Barry* and family in action:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dependency-injection/alice.png" alt="Alice in action"> </img>
|
||||
<img src="assets/images/cookbooks/dependency-injection/alice.png" alt="Alice in action"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -133,6 +133,6 @@ Two sample components and the `AdComponent` interface are shown below:
|
|||
|
||||
The final ad banner looks like this:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
|
||||
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
|
||||
</figure>
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ This proves that any user input is bound back to the data model.
|
|||
Saving and retrieving the data is an exercise for another time.
|
||||
The final form looks like this:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> </img>
|
||||
<img src="assets/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> </img>
|
||||
</figure>
|
||||
|
||||
[Back to top](#top)
|
|
@ -23,7 +23,7 @@ This cookbook explains how to do it.**See the <live-example name="cb-set-documen
|
|||
|
||||
|
||||
<td>
|
||||
<img src='/resources/images/devguide/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"> </img> <br> </br> <img src='/resources/images/devguide/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"> </img>
|
||||
<img src='assets/images/devguide/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"> </img> <br> </br> <img src='assets/images/devguide/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"> </img>
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ Let's inject the `Title` service into the root `AppComponent` and expose a binda
|
|||
|
||||
We bind that method to three anchor tags and, voilà!
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/cookbooks/set-document-title/set-title-anim.gif" alt="Set title"> </img>
|
||||
<img src="assets/images/cookbooks/set-document-title/set-title-anim.gif" alt="Set title"> </img>
|
||||
</figure>
|
||||
|
||||
Here's the complete solution
|
||||
|
|
|
@ -48,7 +48,7 @@ The examples in this page are available as a <live-example></live-example>.
|
|||
|
||||
## Quickstart example: Transitioning between two states
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
</figure>
|
||||
|
||||
You can build a simple animation that transitions an element between two states
|
||||
|
@ -121,7 +121,7 @@ controls the timing of switching between one set of styles and the next:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"> </img>
|
||||
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"> </img>
|
||||
</figure>
|
||||
|
||||
If several transitions have the same timing configuration, you can combine
|
||||
|
@ -154,7 +154,7 @@ transitions that apply regardless of which state the animation is in. For exampl
|
|||
* The `* => *` transition applies when *any* change between two states takes place.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"> </img>
|
||||
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"> </img>
|
||||
</figure>
|
||||
|
||||
### The `void` state
|
||||
|
@ -168,14 +168,14 @@ For example the `* => void` transition applies when the element leaves the view,
|
|||
regardless of what state it was in before it left.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"> </img>
|
||||
<img src="assets/images/devguide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"> </img>
|
||||
</figure>
|
||||
|
||||
The wildcard state `*` also matches `void`.
|
||||
|
||||
## Example: Entering and leaving
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"> </img>
|
||||
</figure>
|
||||
|
||||
Using the `void` and `*` states you can define transitions that animate the
|
||||
|
@ -184,6 +184,8 @@ entering and leaving of elements:
|
|||
* Enter: `void => *`
|
||||
* Leave: `* => void`
|
||||
|
||||
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'}
|
||||
|
||||
|
@ -201,7 +203,7 @@ These two common animations have their own aliases:
|
|||
|
||||
## Example: Entering and leaving from different states
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"> </img>
|
||||
<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>
|
||||
</figure>
|
||||
|
||||
You can also combine this animation with the earlier state transition animation by
|
||||
|
@ -217,7 +219,7 @@ is:
|
|||
This gives you fine-grained control over each transition:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"> </img>
|
||||
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -245,7 +247,7 @@ If you don't provide a unit when specifying dimension, Angular assumes the defau
|
|||
|
||||
## Automatic property calculation
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
</figure>
|
||||
|
||||
Sometimes you don't know the value of a dimensional style property until runtime.
|
||||
|
@ -297,21 +299,22 @@ and the delay (or as the *second* value when there is no delay):
|
|||
* Run for 200ms, with easing: `'0.2s ease-in-out'`
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
</figure>
|
||||
|
||||
### Example
|
||||
|
||||
Here are a couple of custom timings in action. Both enter and leave last for
|
||||
200 milliseconds but they have different easings. The leave begins after a
|
||||
slight delay:
|
||||
200 milliseconds, that is `0.2s`, but they have different easings. The leave begins after a
|
||||
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'}
|
||||
|
||||
## Multi-step animations with keyframes
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
</figure>
|
||||
|
||||
Animation *keyframes* go beyond a simple transition to a more intricate animation
|
||||
|
@ -336,7 +339,7 @@ spacing are automatically assigned. For example, three keyframes without predefi
|
|||
offsets receive offsets `0`, `0.5`, and `1`.
|
||||
## Parallel animation groups
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
<img src="assets/images/devguide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"> </img>
|
||||
</figure>
|
||||
|
||||
You've seen how to animate multiple style properties at the same time:
|
||||
|
@ -358,7 +361,7 @@ One group animates the element transform and width; the other group animates the
|
|||
|
||||
A callback is fired when an animation is started and also when it is done.
|
||||
|
||||
In the keyframes example, you have a `trigger` called `@flyInOut`. There you can hook
|
||||
In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
|
||||
those callbacks like this:
|
||||
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ This file is very stable. Once you've set it up, you may never change it again.
|
|||
|
||||
Your initial app has only a single module, the _root_ module.
|
||||
As your app grows, you'll consider subdividing it into multiple "feature" modules,
|
||||
so of which can be loaded later ("lazy loaded") if and when the user chooses
|
||||
some of which can be loaded later ("lazy loaded") if and when the user chooses
|
||||
to visit those features.
|
||||
|
||||
When you're ready to explore these possibilities, visit the [Angular Modules (NgModule)](ngmodule.html) guide.
|
|
@ -17,7 +17,7 @@ Of course, there is more to it than this.
|
|||
You'll learn the details in the pages that follow. For now, focus on the big picture.
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"> </img>
|
||||
<img src="assets/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"> </img>
|
||||
</figure>
|
||||
|
||||
The architecture diagram identifies the eight main building blocks of an Angular application:
|
||||
|
@ -41,13 +41,13 @@ Learn these building blocks, and you're on your way.
|
|||
|
||||
## Modules
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
### Angular libraries
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -59,7 +59,7 @@ Learn these building blocks, and you're on your way.
|
|||
## Components
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
A _component_ controls a patch of screen called a *view*.
|
||||
|
@ -87,7 +87,7 @@ Your app can take action at each moment in this lifecycle through optional [life
|
|||
|
||||
## Templates
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
You define a component's view with its companion **template**. A template is a form of HTML
|
||||
|
@ -110,7 +110,7 @@ hero that the user selects from the list presented by the `HeroListComponent`.
|
|||
The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
|
||||
|
@ -122,7 +122,7 @@ Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom
|
|||
|
||||
## Metadata
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
|
||||
|
@ -150,7 +150,7 @@ This is one way to tell Angular that the component's constructor requires a `Her
|
|||
so it can get the list of heroes to display.
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
The metadata in the `@Component` tells Angular where to get the major building blocks you specify for the component.
|
||||
|
@ -171,7 +171,7 @@ Without a framework, you would be responsible for pushing data values into the H
|
|||
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
|
||||
read as any experienced jQuery programmer can attest.
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"> </img>
|
||||
<img src="assets/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"> </img>
|
||||
</figure>
|
||||
|
||||
Angular supports **data binding**,
|
||||
|
@ -198,13 +198,13 @@ Angular processes *all* data bindings once per JavaScript event cycle,
|
|||
from the root of the application component tree through all child components.
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Data binding plays an important role in communication
|
||||
between a template and its component.<br class="l-clear-both">
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Data binding is also important for communication between parent and child components.<br class="l-clear-both">
|
||||
|
@ -215,7 +215,7 @@ Data binding is also important for communication between parent and child compon
|
|||
|
||||
## Directives
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
|
||||
|
@ -260,7 +260,7 @@ Of course, you can also write your own directives. Components such as
|
|||
|
||||
## Services
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
_Service_ is a broad category encompassing any value, function, or feature that your application needs.
|
||||
|
@ -305,7 +305,7 @@ application logic into services and make those services available to components
|
|||
|
||||
## Dependency injection
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
_Dependency injection_ is a way to supply a new instance of a class
|
||||
|
@ -324,7 +324,7 @@ This is *dependency injection*.
|
|||
|
||||
The process of `HeroService` injection looks a bit like this:
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/architecture/injector-injects.png" alt="Service"> </img>
|
||||
<img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"> </img>
|
||||
</figure>
|
||||
|
||||
If the injector doesn't have a `HeroService`, how does it know how to make one?
|
||||
|
|
|
@ -117,7 +117,7 @@ recognizes the directive when it encounters `myHighlight` in the template.
|
|||
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight"> </img>
|
||||
<img src="assets/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -180,7 +180,7 @@ Here's the updated directive in full:
|
|||
Run the app and confirm that the background color appears when the mouse hovers over the `p` and
|
||||
disappears as it moves out.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"> </img>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -260,7 +260,7 @@ lets you pick the highlight color with a radio button and bind your color choice
|
|||
Update `app.component.html` as follows:
|
||||
Revise the `AppComponent.color` so that it has no initial value.Here is the harness and directive in action.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"> </img>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -292,7 +292,7 @@ because you made it _public_ with the `@Input` !{_decorator}.
|
|||
|
||||
Here's how the harness should work when you're done coding.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"> </img>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ An annotated history of recent documentation improvements.
|
|||
The Angular documentation is a living document with continuous improvements.
|
||||
This log calls attention to recent significant changes.
|
||||
|
||||
## 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.
|
||||
|
||||
## Template Syntax/Structural Directives: refreshed (2017-02-06)
|
||||
The [_Template-Syntax_](template-syntax.html) and [_Structural Directives_](structural-directives.html)
|
||||
guides were significantly revised for clarity, accuracy, and current recommended practices.
|
||||
|
|
|
@ -5,100 +5,96 @@ Component Styles
|
|||
Learn how to apply CSS styles to components.
|
||||
|
||||
@description
|
||||
Angular applications are styled with regular CSS. That means we can apply
|
||||
everything we know about CSS stylesheets, selectors, rules, and media queries
|
||||
to our Angular applications directly.
|
||||
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.
|
||||
|
||||
On top of this, Angular has the ability to bundle *component styles*
|
||||
with our components enabling a more modular design than regular stylesheets.
|
||||
Additionally, Angular can bundle *component styles*
|
||||
with components, enabling a more modular design than regular stylesheets.
|
||||
|
||||
In this chapter we learn how to load and apply these *component styles*.
|
||||
This page describes how to load and apply these component styles.
|
||||
|
||||
## Table Of Contents
|
||||
|
||||
* [Using Component Styles](#using-component-styles)
|
||||
* [Using component styles](#using-component-styles)
|
||||
* [Special selectors](#special-selectors)
|
||||
* [Loading Styles into Components](#loading-styles)
|
||||
* [Controlling View Encapsulation: Emulated, Native, and None](#view-encapsulation)
|
||||
* [Appendix 1: Inspecting the generated runtime component styles](#inspect-generated-css)
|
||||
* [Appendix 2: Loading Styles with Relative URLs](#relative-urls)
|
||||
* [Loading styles into components](#loading-styles)
|
||||
* [Controlling view encapsulation: native, emulated, and none](#view-encapsulation)
|
||||
* [Appendix 1: Inspecting the CSS generated in emulated view encapsulation](#inspect-generated-css)
|
||||
* [Appendix 2: Loading styles with relative URLs](#relative-urls)
|
||||
|
||||
Run the <live-example></live-example> of the code shown in this chapter.
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
## Using Component Styles
|
||||
## Using component styles
|
||||
|
||||
For every Angular component we write, we may define not only an HTML template,
|
||||
For every Angular component you write, you may define not only an HTML template,
|
||||
but also the CSS styles that go with that template,
|
||||
specifying any selectors, rules, and media queries that we need.
|
||||
specifying any selectors, rules, and media queries that you need.
|
||||
|
||||
One way to do this is to set the `styles` property in the component metadata.
|
||||
The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
|
||||
Usually we give it one string as in this example:
|
||||
Usually you give it one string, as in the following example:
|
||||
|
||||
|
||||
{@example 'component-styles/ts/src/app/hero-app.component.ts'}
|
||||
|
||||
Component styles differ from traditional, global styles in a couple of ways.
|
||||
|
||||
Firstly, the selectors we put into a component's styles *only apply within the template
|
||||
of that component*. The `h1` selector in the example above only applies to the `<h1>` tag
|
||||
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
|
||||
in the template of `HeroAppComponent`. Any `<h1>` elements elsewhere in
|
||||
the application are unaffected.
|
||||
|
||||
This is a big improvement in modularity compared to how CSS traditionally works:
|
||||
This is a big improvement in modularity compared to how CSS traditionally works.
|
||||
|
||||
1. We can use the CSS class names and selectors that make the most sense in the context of each component.
|
||||
|
||||
1. Class names and selectors are local to the component and won't collide with
|
||||
classes and selectors used elsewhere in the application.
|
||||
|
||||
1. Our component's styles *cannot* be changed by changes to styles elsewhere in the application.
|
||||
|
||||
1. We can co-locate the CSS code of each component with the TypeScript and HTML code of the component,
|
||||
which leads to a neat and tidy project structure.
|
||||
|
||||
1. We can change or remove component CSS code in the future without trawling through the
|
||||
whole application to see where else it may have been used. We just look at the component we're in.
|
||||
* You can use the CSS class names and selectors that make the most sense in the context of each component.
|
||||
* Class names and selectors are local to the component and don't collide with
|
||||
classes and selectors used elsewhere in the application.
|
||||
* Changes to styles elsewhere in the application don't affect the component's styles.
|
||||
* You can co-locate the CSS code of each component with the TypeScript and HTML code of the component,
|
||||
which leads to a neat and tidy project structure.
|
||||
* You can change or remove component CSS code without searching through the
|
||||
whole application to find where else the code is used.
|
||||
|
||||
|
||||
{@a special-selectors}
|
||||
|
||||
## Special selectors
|
||||
|
||||
Component styles have a few special *selectors* from the world of
|
||||
[shadow DOM style scoping](https://www.w3.org/TR/css-scoping-1):
|
||||
Component styles have a few special *selectors* from the world of shadow DOM style scoping
|
||||
(described in the [CSS Scoping Module Level 1](https://www.w3.org/TR/css-scoping-1) page on the
|
||||
[W3C](https://www.w3.org) site).
|
||||
The following sections describe these selectors.
|
||||
|
||||
### :host
|
||||
|
||||
Use the `:host` pseudo-class selector to target styles in the element that *hosts* the component (as opposed to
|
||||
targeting elements *inside* the component's template):
|
||||
targeting elements *inside* the component's template).
|
||||
|
||||
|
||||
{@example 'component-styles/ts/src/app/hero-details.component.css' region='host'}
|
||||
|
||||
This is the *only* way we can target the host element. We cannot reach
|
||||
it from inside the component with other selectors, because it is not part of the
|
||||
component's own template. It is in a parent component's template.
|
||||
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
|
||||
component's own template. The host element is in a parent component's template.
|
||||
|
||||
Use the *function form* to apply host styles conditionally by
|
||||
including another selector inside parentheses after `:host`.
|
||||
|
||||
In the next example we target the host element again, but only when it also has the `active` CSS class.
|
||||
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'}
|
||||
|
||||
### :host-context
|
||||
|
||||
Sometimes it is useful to apply styles based on some condition *outside* a component's view.
|
||||
For example, there may be a CSS theme class applied to the document `<body>` element, and
|
||||
we want to change how our component looks based on that.
|
||||
Sometimes it's useful to apply styles based on some condition *outside* of a component's view.
|
||||
For example, a CSS theme class could be applied to the document `<body>` element, and
|
||||
you want to change how your component looks based on that.
|
||||
|
||||
Use the `:host-context()` pseudo-class selector. It works just like the function
|
||||
form of `:host()`. It looks for a CSS class in *any ancestor* of the component host element, all the way
|
||||
up to the document root. It's useful when combined with another selector.
|
||||
Use the `:host-context()` pseudo-class selector, which works just like the function
|
||||
form of `:host()`. The `:host-context()` selector looks for a CSS class in any ancestor of the component host element,
|
||||
up to the document root. The `:host-context()` selector is useful when combined with another selector.
|
||||
|
||||
In the following example, we apply a `background-color` style to all `<h2>` elements *inside* the component, only
|
||||
The following example applies a `background-color` style to all `<h2>` elements *inside* the component, only
|
||||
if some ancestor element has the CSS class `theme-light`.
|
||||
|
||||
|
||||
|
@ -108,24 +104,23 @@ if some ancestor element has the CSS class `theme-light`.
|
|||
|
||||
Component styles normally apply only to the HTML in the component's own template.
|
||||
|
||||
We can use the `/deep/` selector to force a style down through the child component tree into all the child component views.
|
||||
The `/deep/` selector works to any depth of nested components, and it applies *both to the view
|
||||
children and the content children* of the component.
|
||||
Use the `/deep/` selector to force a style down through the child component tree into all the child component views.
|
||||
The `/deep/` selector works to any depth of nested components, and it applies to both the view
|
||||
children and content children of the component.
|
||||
|
||||
In this example, we target all `<h3>` elements, from the host element down
|
||||
through this component to all of its child elements in the DOM:
|
||||
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'}
|
||||
|
||||
The `/deep/` selector also has the alias `>>>`. We can use either of the two interchangeably.
|
||||
The `/deep/` selector also has the alias `>>>`. You can use either interchangeably.
|
||||
|
||||
|
||||
~~~ {.alert.is-important}
|
||||
|
||||
The `/deep/` and `>>>` selectors should only be used with **emulated** view encapsulation.
|
||||
This is the default and it is what we use most of the time. See the
|
||||
[Controlling View Encapsulation](#view-encapsulation)
|
||||
section for more details.
|
||||
Use the `/deep/` and `>>>` selectors only with *emulated* view encapsulation.
|
||||
Emulated is the default and most commonly used view encapsulation. For more information, see the
|
||||
[Controlling view encapsulation](#view-encapsulation) section.
|
||||
|
||||
|
||||
~~~
|
||||
|
@ -134,53 +129,54 @@ section for more details.
|
|||
|
||||
{@a loading-styles}
|
||||
|
||||
## Loading Styles into Components
|
||||
## Loading styles into components
|
||||
|
||||
We have several ways to add styles to a component:
|
||||
* inline in the template HTML
|
||||
* by setting `styles` or `styleUrls` metadata
|
||||
* with CSS imports
|
||||
There are several ways to add styles to a component:
|
||||
* By setting `styles` or `styleUrls` metadata.
|
||||
* Inline in the template HTML.
|
||||
* With CSS imports.
|
||||
|
||||
The scoping rules outlined above apply to each of these loading patterns.
|
||||
The scoping rules outlined earlier apply to each of these loading patterns.
|
||||
|
||||
### Styles in Metadata
|
||||
### Styles in metadata
|
||||
|
||||
We can add a `styles` #{_array} property to the `@Component` #{_decorator}.
|
||||
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'}
|
||||
|
||||
### Template Inline Styles
|
||||
### Style URLs in metadata
|
||||
|
||||
We 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'}
|
||||
|
||||
### Style URLs in Metadata
|
||||
|
||||
We can load styles from external CSS files by adding a `styleUrls` attribute
|
||||
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'}
|
||||
|
||||
### Template Link Tags
|
||||
### Template inline styles
|
||||
|
||||
We can also embed `<link>` tags into the component's HTML template.
|
||||
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'}
|
||||
|
||||
### Template link tags
|
||||
|
||||
You can also embed `<link>` tags into the component's HTML template.
|
||||
|
||||
As with `styleUrls`, the link tag's `href` URL is relative to the
|
||||
application root, not relative to the component file.
|
||||
application root, not the component file.
|
||||
|
||||
|
||||
{@example 'component-styles/ts/src/app/hero-team.component.ts' region='stylelink'}
|
||||
|
||||
### CSS @imports
|
||||
|
||||
We can also import CSS files into our CSS files by using the standard CSS
|
||||
[`@import` rule](https://developer.mozilla.org/en/docs/Web/CSS/@import).
|
||||
You can also import CSS files into the CSS files using the standard CSS `@import` rule.
|
||||
For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import)
|
||||
on the [MDN](https://developer.mozilla.org) site.
|
||||
|
||||
|
||||
{@example 'component-styles/ts/src/app/hero-details.component.css' region='import'}
|
||||
|
@ -189,48 +185,49 @@ We can also import CSS files into our CSS files by using the standard CSS
|
|||
|
||||
{@a view-encapsulation}
|
||||
|
||||
## Controlling View Encapsulation: Native, Emulated, and None
|
||||
## Controlling view encapsulation: native, emulated, and none
|
||||
|
||||
As discussed above, component CSS styles are *encapsulated* into the component's own view and do
|
||||
not affect the rest of the application.
|
||||
As discussed earlier, component CSS styles are encapsulated into the component's view and don't
|
||||
affect the rest of the application.
|
||||
|
||||
We can control how this encapsulation happens on a *per
|
||||
component* basis by setting the *view encapsulation mode* in the component metadata. There
|
||||
are three modes to choose from:
|
||||
To control how this encapsulation happens on a *per
|
||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
* `Native` view encapsulation uses the browser's native [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
implementation to attach a Shadow DOM to the component's host element, and then puts the component
|
||||
view inside that Shadow DOM. The component's styles are included within the Shadow DOM.
|
||||
|
||||
* `Emulated` view encapsulation (**the default**) emulates the behavior of Shadow DOM by preprocessing
|
||||
* `Native` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
See [Appendix 1](#inspect-generated-css) for details.
|
||||
|
||||
For details, see [Appendix 1](#inspect-generated-css).
|
||||
* `None` means that Angular does no view encapsulation.
|
||||
Angular adds the CSS to the global styles.
|
||||
The scoping rules, isolations, and protections discussed earlier do not apply.
|
||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
||||
This is essentially the same as pasting the component's styles into the HTML.
|
||||
|
||||
Set the components encapsulation mode using the `encapsulation` property in the component metadata:
|
||||
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'}
|
||||
|
||||
`Native` view encapsulation only works on [browsers that have native support
|
||||
for Shadow DOM](http://caniuse.com/#feat=shadowdom). The support is still limited,
|
||||
`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
|
||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
||||
in most cases.
|
||||
|
||||
|
||||
{@a inspect-generated-css}
|
||||
|
||||
## Appendix 1: Inspecting The CSS Generated in Emulated View Encapsulation
|
||||
## Appendix 1: Inspecting the CSS generated in emulated view encapsulation
|
||||
|
||||
When using the default emulated view encapsulation, Angular preprocesses
|
||||
all component styles so that they approximate the standard Shadow CSS scoping rules.
|
||||
When using emulated view encapsulation, Angular preprocesses
|
||||
all component styles so that they approximate the standard shadow CSS scoping rules.
|
||||
|
||||
When we inspect the DOM of a running Angular application with emulated view
|
||||
encapsulation enabled, we see that each DOM element has some extra attributes
|
||||
In the DOM of a running Angular application with emulated view
|
||||
encapsulation enabled, each DOM element has some extra attributes
|
||||
attached to it:
|
||||
|
||||
<code-example format="">
|
||||
|
@ -243,16 +240,15 @@ attached to it:
|
|||
|
||||
</code-example>
|
||||
|
||||
We see two kinds of generated attributes:
|
||||
* An element that would be a Shadow DOM host in native encapsulation has a
|
||||
There are two kinds of generated attributes:
|
||||
* An element that would be a shadow DOM host in native encapsulation has a
|
||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
||||
|
||||
* An element within a component's view has a `_ngcontent` attribute
|
||||
that identifies to which host's emulated Shadow DOM this element belongs.
|
||||
that identifies to which host's emulated shadow DOM this element belongs.
|
||||
|
||||
The exact values of these attributes are not important. They are automatically
|
||||
generated and we never refer to them in application code. But they are targeted
|
||||
by the generated component styles, which we'll find in the `<head>` section of the DOM:
|
||||
The exact values of these attributes aren't important. They are automatically
|
||||
generated and you never refer to them in application code. But they are targeted
|
||||
by the generated component styles, which are in the `<head>` section of the DOM:
|
||||
|
||||
<code-example format="">
|
||||
[_nghost-pmm-5] {
|
||||
|
@ -267,16 +263,14 @@ by the generated component styles, which we'll find in the `<head>` section of t
|
|||
|
||||
</code-example>
|
||||
|
||||
These are the styles we wrote, post-processed so that each selector is augmented
|
||||
These styles are post-processed so that each selector is augmented
|
||||
with `_nghost` or `_ngcontent` attribute selectors.
|
||||
These extra selectors enable the scoping rules described in this guide.
|
||||
|
||||
We'll likely live with *emulated* mode until shadow DOM gains traction.
|
||||
These extra selectors enable the scoping rules described in this page.
|
||||
|
||||
|
||||
{@a relative-urls}
|
||||
|
||||
## Appendix 2: Loading Styles with Relative URLs
|
||||
## Appendix 2: Loading styles with relative URLs
|
||||
|
||||
It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory:
|
||||
<code-example format="nocode">
|
||||
|
@ -286,6 +280,6 @@ It's common practice to split a component's code, HTML, and CSS into three separ
|
|||
|
||||
</code-example>
|
||||
|
||||
We include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
|
||||
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.
|
||||
|
|
|
@ -342,7 +342,7 @@ See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API:
|
|||
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.
|
||||
For example, when the URL to load the app is something like `http://www.mysite.com/mysrc/app/`,
|
||||
For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`,
|
||||
the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`.
|
||||
|
||||
When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors
|
||||
|
|
|
@ -14,7 +14,7 @@ conditionally show a message below the list.
|
|||
The final UI looks like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/displaying-data/final.png" alt="Final UI"> </img>
|
||||
<img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"> </img>
|
||||
</figure>
|
||||
|
||||
# Contents
|
||||
|
@ -63,7 +63,7 @@ inside the `<my-app>` tag.
|
|||
|
||||
Now run the app. It should display the title and hero name:
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img>
|
||||
<img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img>
|
||||
</figure>
|
||||
|
||||
## Template inline or template file?
|
||||
|
@ -111,7 +111,7 @@ In this case, `ngFor` is displaying !{_an} !{_array}, but `ngFor` can
|
|||
repeat items for any [iterable](!{_iterableUrl}) object.Now the heroes appear in an unordered list.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
|
||||
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -5,74 +5,73 @@ Forms
|
|||
A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.
|
||||
|
||||
@description
|
||||
We’ve all used a form to log in, submit a help request, place an order, book a flight,
|
||||
schedule a meeting, and perform countless other data entry tasks.
|
||||
Forms are the mainstay of business applications.
|
||||
You use forms to log in, submit a help request, place an order, book a flight,
|
||||
schedule a meeting, and perform countless other data-entry tasks.
|
||||
|
||||
Any seasoned web developer can slap together an HTML form with all the right tags.
|
||||
It's more challenging to create a cohesive data entry experience that guides the
|
||||
user efficiently and effectively through the workflow behind the form.
|
||||
In developing a form, it's important to create a data-entry experience that guides the
|
||||
user efficiently and effectively through the workflow.
|
||||
|
||||
*That* takes design skills that are, to be frank, well out of scope for this guide.
|
||||
Developing forms requires design skills (which are out of scope for this page), as well as framework support for
|
||||
*two-way data binding, change tracking, validation, and error handling*,
|
||||
which you'll learn about on this page.
|
||||
|
||||
It also takes framework support for
|
||||
**two-way data binding, change tracking, validation, and error handling**
|
||||
... which we shall cover in this guide on Angular forms.
|
||||
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
||||
|
||||
We will build a simple form from scratch, one step at a time. Along the way we'll learn how to:
|
||||
- Build an Angular form with a component and template.
|
||||
- Use `ngModel` to create two-way data bindings for reading and writing input-control values.
|
||||
- Track state changes and the validity of form controls.
|
||||
- Provide visual feedback using special CSS classes that track the state of the controls.
|
||||
- Display validation errors to users and enable/disable form controls.
|
||||
- Share information across HTML elements using template reference variables.
|
||||
|
||||
- Build an Angular form with a component and template
|
||||
- Use `ngModel` to create two-way data bindings for reading and writing input control values
|
||||
- Track state changes and the validity of form controls
|
||||
- Provide visual feedback using special CSS classes that track the state of the controls
|
||||
- Display validation errors to users and enable/disable form controls
|
||||
- Share information across HTML elements using template reference variables
|
||||
|
||||
Run the <live-example></live-example>.
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
## Template-driven forms
|
||||
|
||||
Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
|
||||
the form-specific directives and techniques described in this guide.
|
||||
You can build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
|
||||
the form-specific directives and techniques described in this page.
|
||||
|
||||
That's not the only way to create a form but it's the way we'll cover in this guide.We can build almost any form we need with an Angular template — login forms, contact forms, pretty much any business form.
|
||||
We can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
||||
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—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.
|
||||
|
||||
It will be pretty easy because Angular handles many of the repetitive, boilerplate tasks we'd
|
||||
otherwise wrestle with ourselves.
|
||||
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
|
||||
otherwise wrestle with yourself.
|
||||
|
||||
We'll discuss and learn to build a template-driven form that looks like this:
|
||||
You'll learn to build a template-driven form that looks like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form"> </img>
|
||||
<img src="assets/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form"> </img>
|
||||
</figure>
|
||||
|
||||
Here at the *Hero Employment Agency* we use this form to maintain personal information about heroes.
|
||||
Every hero needs a job. It's our company mission to match the right hero with the right crisis!
|
||||
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
||||
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
|
||||
|
||||
Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
|
||||
|
||||
If we delete the hero name, the form displays a validation error in an attention-grabbing style:
|
||||
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"> </img>
|
||||
<img src="assets/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"> </img>
|
||||
</figure>
|
||||
|
||||
Note that the submit button is disabled, and the "required" bar to the left of the input control changed from green to red.
|
||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||
|
||||
We'll customize the colors and location of the "required" bar with standard CSS.
|
||||
We'll build this form in small steps:
|
||||
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.
|
||||
1. Create the component that controls the form.
|
||||
1. Create a template with the initial form layout.
|
||||
1. Bind data properties to each form control using the `ngModel` two-way data binding syntax.
|
||||
1. Add a `name` attribute to each form input control.
|
||||
1. Bind data properties to each form control using the `ngModel` two-way data-binding syntax.
|
||||
1. Add a `name` attribute to each form-input control.
|
||||
1. Add custom CSS to provide visual feedback.
|
||||
1. Show and hide validation error messages.
|
||||
1. Handle form submission with **ngSubmit**.
|
||||
1. Disable the form’s submit button until the form is valid.
|
||||
1. Show and hide validation-error messages.
|
||||
1. Handle form submission with *ngSubmit*.
|
||||
1. Disable the form’s *Submit* button until the form is valid.
|
||||
## Setup
|
||||
|
||||
Follow the [setup](setup.html) instructions for creating a new project
|
||||
|
@ -80,11 +79,11 @@ named <span ngio-ex>angular-forms</span>.
|
|||
|
||||
## Create the Hero model class
|
||||
|
||||
As users enter form data, we'll capture their changes and update an instance of a model.
|
||||
We can't lay out the form until we know what the model looks like.
|
||||
As users enter form data, you'll capture their changes and update an instance of a model.
|
||||
You can't lay out the form until you know what the model looks like.
|
||||
|
||||
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
|
||||
That describes well our `Hero` class with its three required fields (`id`, `name`, `power`)
|
||||
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
|
||||
and one optional field (`alterEgo`).
|
||||
|
||||
In the `!{_appDir}` directory, create the following file with the given content:
|
||||
|
@ -92,60 +91,60 @@ In the `!{_appDir}` directory, create the following file with the given content:
|
|||
|
||||
{@example 'forms/ts/src/app/hero.ts'}
|
||||
|
||||
It's an anemic model with few requirements and no behavior. Perfect for our demo.
|
||||
It's an anemic model with few requirements and no behavior. Perfect for the demo.
|
||||
|
||||
The TypeScript compiler generates a public field for each `public` constructor parameter and
|
||||
assigns the parameter’s value to that field automatically when we create new heroes.
|
||||
automatically assigns the parameter’s value to that field when you create heroes.
|
||||
|
||||
The `alterEgo` is optional, so the constructor lets us omit it; note the (?) in `alterEgo?`.
|
||||
The `alterEgo` is optional, so the constructor lets you omit it; note the question mark (?) in `alterEgo?`.
|
||||
|
||||
We can create a new hero like this:
|
||||
You can create a new hero like this:
|
||||
|
||||
## Create a form component
|
||||
|
||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||
to handle data and user interactions programmatically.
|
||||
We begin with the class because it states, in brief, what the hero editor can do.
|
||||
Begin with the class because it states, in brief, what the hero editor can do.
|
||||
|
||||
Create the following file with the given content:
|
||||
There’s nothing special about this component, nothing form-specific,
|
||||
nothing to distinguish it from any component we've written before.
|
||||
nothing to distinguish it from any component you've written before.
|
||||
|
||||
Understanding this component requires only the Angular concepts covered in previous guides.
|
||||
Understanding this component requires only the Angular concepts covered in previous pages.
|
||||
|
||||
1. The code imports the Angular core library, and the `Hero` model we just created.
|
||||
1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
|
||||
1. The `moduleId: module.id` property sets the base for module-relative loading of the `templateUrl`.
|
||||
1. The `templateUrl` property points to a separate file for the template HTML.
|
||||
1. We defined dummy data for `model` and `powers`, as befits a demo.
|
||||
Down the road, we can inject a data service to get and save real data
|
||||
or perhaps expose these properties as
|
||||
[inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
|
||||
parent component. None of this concerns us now and these future changes won't affect our form.
|
||||
1. We threw in a `diagnostic` property to return a JSON representation of our model.
|
||||
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
|
||||
- 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
|
||||
or perhaps expose these properties as inputs and outputs
|
||||
(see [Input and output properties](./template-syntax.html#inputs-outputs) on the
|
||||
[Template Syntax](./template-syntax.html) page) for binding to a
|
||||
parent component. This is not a concern now and these future changes won't affect the form.
|
||||
- You added a `diagnostic` property to return a JSON representation of the model.
|
||||
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
||||
|
||||
### Why the separate template file?
|
||||
|
||||
Why don't we write the template inline in the component file as we often do elsewhere?
|
||||
Why don't you write the template inline in the component file as you often do elsewhere?
|
||||
|
||||
There is no “right” answer for all occasions. We like inline templates when they are short.
|
||||
Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to
|
||||
write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
|
||||
We also like short files with a clear and obvious purpose like this one.
|
||||
There is no "right" answer for all occasions. Inline templates are useful when they are short.
|
||||
Most form templates aren't short. TypeScript and JavaScript files generally aren't the best place to
|
||||
write (or read) large stretches of HTML, and few editors help with files that have a mix of HTML and code.
|
||||
|
||||
Form templates tend to be quite large even when displaying a small number of fields
|
||||
Form templates tend to be large, even when displaying a small number of fields,
|
||||
so it's usually best to put the HTML template in a separate file.
|
||||
We'll write that template file in a moment. Before we do, we'll take a step back
|
||||
and revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
||||
You'll write that template file in a moment. First,
|
||||
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
||||
|
||||
## Revise *app.module.ts*
|
||||
|
||||
`app.module.ts` defines the application's root module. In it we identify the external modules we'll use in our application
|
||||
and declare the components that belong to this module, such as our `HeroFormComponent`.
|
||||
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
|
||||
and declare the components that belong to this module, such as the `HeroFormComponent`.
|
||||
|
||||
Because template-driven forms are in their own module, we need to add the `FormsModule` to the array of
|
||||
`imports` for our application module before we can use forms.
|
||||
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
|
||||
`imports` for the application module before you can use forms.
|
||||
|
||||
Replace the contents of the "QuickStart" version with the following:
|
||||
|
||||
|
@ -154,19 +153,19 @@ Replace the contents of the "QuickStart" version with the following:
|
|||
|
||||
There are three changes:
|
||||
|
||||
1. We import `FormsModule` and our new `HeroFormComponent`.
|
||||
1. You import `FormsModule` and the new `HeroFormComponent`.
|
||||
|
||||
1. We add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives our application
|
||||
1. You add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives the application
|
||||
access to all of the template-driven forms features, including `ngModel`.
|
||||
|
||||
1. We add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes
|
||||
1. You add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes
|
||||
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.
|
||||
If you wrote it and it should belong to this module, _DO_ declare it in the `declarations` array.
|
||||
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
|
||||
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
|
||||
|
||||
|
||||
~~~
|
||||
|
@ -174,7 +173,7 @@ If you wrote it and it should belong to this module, _DO_ declare it in th
|
|||
|
||||
## Revise *app.component.ts*
|
||||
|
||||
`AppComponent` is the application's root component. It will host our new `HeroFormComponent`.
|
||||
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
|
||||
|
||||
Replace the contents of the "QuickStart" version with the following:
|
||||
|
||||
|
@ -184,140 +183,139 @@ Replace the contents of the "QuickStart" version with the following:
|
|||
|
||||
There are only two changes.
|
||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
||||
This will display the hero form when the application component is loaded.
|
||||
We've also dropped the `name` field from the class body.
|
||||
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 new template file with the following contents:
|
||||
Create the template file with the following contents:
|
||||
|
||||
|
||||
{@example 'forms/ts/src/app/hero-form.component.html' region='start'}
|
||||
|
||||
That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
||||
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.
|
||||
|
||||
The *Name* `<input>` control has the HTML5 `required` attribute;
|
||||
the *Alter Ego* `<input>` control does not because `alterEgo` is optional.
|
||||
|
||||
We've got a *Submit* button at the bottom with some classes on it for styling.
|
||||
You added a *Submit* button at the bottom with some classes on it for styling.
|
||||
|
||||
**We are not using Angular yet**. There are no bindings, no extra directives, just layout.
|
||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
||||
|
||||
The `container`, `form-group`, `form-control`, and `btn` classes
|
||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
|
||||
We're using Bootstrap to give the form a little style!
|
||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
||||
Bootstrap gives the form a little style.
|
||||
|
||||
|
||||
~~~ {.callout.is-important}
|
||||
|
||||
|
||||
<header>
|
||||
Angular forms do not require a style library
|
||||
Angular forms don't require a style library
|
||||
</header>
|
||||
|
||||
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
|
||||
the styles of any external library. Angular apps can use any CSS library, or none at all.
|
||||
the styles of any external library. Angular apps can use any CSS library or none at all.
|
||||
|
||||
|
||||
~~~
|
||||
|
||||
Let's add the stylesheet. Open `index.html` and add the following link to the `<head>`:
|
||||
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
|
||||
|
||||
## Add powers with _*ngFor_
|
||||
|
||||
Our hero must choose one super power from a fixed list of Agency-approved powers.
|
||||
We maintain that list internally (in `HeroFormComponent`).
|
||||
The hero must choose one superpower from a fixed list of agency-approved powers.
|
||||
You maintain that list internally (in `HeroFormComponent`).
|
||||
|
||||
We'll add a `select` to our
|
||||
You'll add a `select` to the
|
||||
form and bind the options to the `powers` list using `ngFor`,
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) guide.
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) page.
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||
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;
|
||||
we display its name using the interpolation syntax.
|
||||
you display its name using the interpolation syntax.
|
||||
|
||||
## Two-way data binding with _ngModel_
|
||||
|
||||
Running the app right now would be disappointing.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"> </img>
|
||||
<img src="assets/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"> </img>
|
||||
</figure>
|
||||
|
||||
We don't see hero data because we are not binding to the `Hero` yet.
|
||||
We know how to do that from earlier guides.
|
||||
[Displaying Data](./displaying-data.html) taught us property binding.
|
||||
[User Input](./user-input.html) showed us how to listen for DOM events with an
|
||||
You don't see hero data because you're not binding to the `Hero` yet.
|
||||
You know how to do that from earlier pages.
|
||||
[Displaying Data](./displaying-data.html) teaches property binding.
|
||||
[User Input](./user-input.html) shows how to listen for DOM events with an
|
||||
event binding and how to update a component property with the displayed value.
|
||||
|
||||
Now we need to display, listen, and extract at the same time.
|
||||
Now you need to display, listen, and extract at the same time.
|
||||
|
||||
We could use the techniques we already know, but
|
||||
instead we'll introduce something new: the `[(ngModel)]` syntax, which
|
||||
makes binding the form to the model super easy.
|
||||
You could use the techniques you already know, but
|
||||
instead you'll use the new `[(ngModel)]` syntax, which
|
||||
makes binding the form to the model easy.
|
||||
|
||||
Find the `<input>` tag for *Name* and update it like this:
|
||||
|
||||
We added a diagnostic interpolation after the input tag
|
||||
so we can see what we're doing.
|
||||
We left ourselves a note to throw it away when we're done.
|
||||
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 we run the app right now and started typing in the *Name* input box,
|
||||
adding and deleting characters, we'd see them appearing and disappearing
|
||||
If you ran the app now and started typing in the *Name* input box,
|
||||
adding and deleting characters, you'd see them appear and disappear
|
||||
from the interpolated text.
|
||||
At some point it might look like this.
|
||||
At some point it might look like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"> </img>
|
||||
<img src="assets/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"> </img>
|
||||
</figure>
|
||||
|
||||
The diagnostic is evidence that values really are flowing from the input box to the model and
|
||||
back again.
|
||||
|
||||
That's **two-way data binding**!
|
||||
For more information about `[(ngModel)]` and two-way data bindings, see
|
||||
the [Template Syntax](template-syntax.html#ngModel) page.
|
||||
Notice that we also added a `name` attribute to our `<input>` tag and set it to "name"
|
||||
That's *two-way data binding*.
|
||||
For more information, see
|
||||
[Two-way binding with NgModel](template-syntax.html#ngModel) on the
|
||||
the [Template Syntax](template-syntax.html) 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.
|
||||
|
||||
Internally Angular creates `FormControl` instances and
|
||||
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 we assigned to the `name` attribute.
|
||||
We'll talk about `NgForm` [later in this guide](#ngForm).
|
||||
Let's add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||
We'll ditch the input box binding message
|
||||
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
||||
Read more in [The NgForm directive](#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.
|
||||
Then we can confirm that two-way data binding works *for the entire hero model*.
|
||||
Then you can confirm that two-way data binding works *for the entire hero model*.
|
||||
|
||||
After revision, the core of our form should look like this:
|
||||
After revision, the core of the form should look like this:
|
||||
|
||||
- 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 we run the app now and changed every hero model property, the form might display like this:
|
||||
If you run the app now and change every hero model property, the form might display like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"> </img>
|
||||
<img src="assets/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"> </img>
|
||||
</figure>
|
||||
|
||||
The diagnostic near the top of the form
|
||||
confirms that all of our changes are reflected in the model.
|
||||
confirms that all of your changes are reflected in the model.
|
||||
|
||||
**Delete** the `{{diagnostic}}` binding at the top as it has served its purpose.
|
||||
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
|
||||
|
||||
## Track control state and validity with _ngModel_
|
||||
|
||||
A form isn't just about data binding. We'd also like to know the state of the controls in our form.
|
||||
|
||||
Using `ngModel` in a form gives us more than just a two way data binding. It also tells
|
||||
us if the user touched the control, if the value changed, or if the value became invalid.
|
||||
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
|
||||
you if the user touched the control, if the value changed, or if the value became invalid.
|
||||
|
||||
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
||||
We can leverage those class names to change the appearance of the control.
|
||||
You can leverage those class names to change the appearance of the control.
|
||||
|
||||
<table>
|
||||
|
||||
|
@ -344,7 +342,7 @@ We can leverage those class names to change the appearance of the control.
|
|||
<tr>
|
||||
|
||||
<td>
|
||||
Control has been visited
|
||||
The control has been visited.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -364,7 +362,7 @@ We can leverage those class names to change the appearance of the control.
|
|||
<tr>
|
||||
|
||||
<td>
|
||||
Control's value has changed
|
||||
The control's value has changed.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -384,7 +382,7 @@ We can leverage those class names to change the appearance of the control.
|
|||
<tr>
|
||||
|
||||
<td>
|
||||
Control's value is valid
|
||||
The control's value is valid.
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -403,10 +401,10 @@ We can leverage those class names to change the appearance of the control.
|
|||
|
||||
</table>
|
||||
|
||||
Let's temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
|
||||
Temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
|
||||
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
||||
Now run the app, and look at the _Name_ input box.
|
||||
Follow the next four steps *precisely*:
|
||||
Now run the app and look at the _Name_ input box.
|
||||
Follow these steps *precisely*:
|
||||
|
||||
1. Look but don't touch.
|
||||
1. Click inside the name box, then click outside it.
|
||||
|
@ -416,32 +414,32 @@ Follow the next four steps *precisely*:
|
|||
The actions and effects are as follows:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition"> </img>
|
||||
<img src="assets/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition"> </img>
|
||||
</figure>
|
||||
|
||||
We should see the following transitions and class names:
|
||||
You should see the following transitions and class names:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"> </img>
|
||||
<img src="assets/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"> </img>
|
||||
</figure>
|
||||
|
||||
The `ng-valid`/`ng-invalid` pair is the most interesting to us, because we want to send a
|
||||
strong visual signal when the values are invalid. We also want to mark required fields.
|
||||
To create such visual feedback, let's add definitions for the `ng-*` CSS classes.
|
||||
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
||||
strong visual signal when the values are invalid. You also want to mark required fields.
|
||||
To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
||||
|
||||
**Delete** the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
||||
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
||||
|
||||
## Add custom CSS for visual feedback
|
||||
|
||||
We can mark required fields and invalid data at the same time with a colored bar
|
||||
You can mark required fields and invalid data at the same time with a colored bar
|
||||
on the left of the input box:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"> </img>
|
||||
<img src="assets/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"> </img>
|
||||
</figure>
|
||||
|
||||
We achieve this effect by adding these class definitions to a new `forms.css` file
|
||||
that we add to our project as a sibling to `index.html`:
|
||||
You achieve this effect by adding these class definitions to a new `forms.css` file
|
||||
that you add to the project as a sibling to `index.html`:
|
||||
|
||||
|
||||
{@example 'forms/ts/src/forms.css'}
|
||||
|
@ -449,52 +447,51 @@ that we add to our project as a sibling to `index.html`:
|
|||
Update the `<head>` of `index.html` to include this style sheet:
|
||||
## Show and hide validation error messages
|
||||
|
||||
We can do better. The _Name_ input box is required and clearing it turns the bar red.
|
||||
That says *something* is wrong but we don't know *what* is wrong or what to do about it.
|
||||
We can leverage the control's state to reveal a helpful message.
|
||||
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
|
||||
That says something is wrong but the user doesn't know *what* is wrong or what to do about it.
|
||||
Leverage the control's state to reveal a helpful message.
|
||||
|
||||
Here's the way it should look when the user deletes the name:
|
||||
When the user deletes the name, the form should look like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="Name required"> </img>
|
||||
<img src="assets/images/devguide/forms/name-required-error.png" width="400px" alt="Name required"> </img>
|
||||
</figure>
|
||||
|
||||
To achieve this effect we extend the `<input>` tag with
|
||||
1. a [template reference variable](./template-syntax.html#ref-vars)
|
||||
1. the "*is required*" message in a nearby `<div>` which we'll display only if the control is invalid.
|
||||
To achieve this effect, extend the `<input>` tag with the following:
|
||||
- A [template reference variable](./template-syntax.html#ref-vars).
|
||||
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
|
||||
|
||||
Here's an example of adding an error message to the _name_ input box:
|
||||
We need a template reference variable to access the input box's Angular control from within the template.
|
||||
Here we created a variable called `name` and gave it the value "ngModel".
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
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".
|
||||
|
||||
Why "ngModel"?
|
||||
A directive's [exportAs](../api/core/index/Directive-decorator.html) property
|
||||
tells Angular how to link the reference variable to the directive.
|
||||
We set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
||||
We control visibility of the name error message by binding properties of the `name`
|
||||
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.
|
||||
In this example, we 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.
|
||||
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.
|
||||
|
||||
This user experience is the developer's choice. Some folks want to see the message at all times.
|
||||
If we ignore the `pristine` state, we would hide the message only when the value is valid.
|
||||
If we arrive in this component with a new (blank) hero or an invalid hero,
|
||||
we'll see the error message immediately, before we've done anything.
|
||||
This user experience is the developer's choice. Some developers want the message to display at all times.
|
||||
If you ignore the `pristine` state, you would hide the message only when the value is valid.
|
||||
If you arrive in this component with a new (blank) hero or an invalid hero,
|
||||
you'll see the error message immediately, before you've done anything.
|
||||
|
||||
Some folks find that behavior disconcerting.
|
||||
They only want to see the message when the user makes an invalid change.
|
||||
Some developers want to the message to display only when the user makes an invalid change.
|
||||
Hiding the message while the control is "pristine" achieves that goal.
|
||||
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
|
||||
You'll see the significance of this choice when you [add a new hero](#new-hero) to the form.
|
||||
|
||||
The hero *Alter Ego* is optional so we can leave that be.
|
||||
The hero *Alter Ego* is optional so you can leave that be.
|
||||
|
||||
Hero *Power* selection is required.
|
||||
We can add the same kind of error handling to the `<select>` if we want,
|
||||
You can add the same kind of error handling to the `<select>` if you want,
|
||||
but it's not imperative because the selection box already constrains the
|
||||
power to valid values.
|
||||
|
||||
We'd like to add a new hero in this form.
|
||||
We place a "New Hero" button at the bottom of the form and bind its click event to a `newHero` component method.
|
||||
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'}
|
||||
|
@ -506,66 +503,66 @@ We place a "New Hero" button at the bottom of the form and bind its click event
|
|||
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.
|
||||
That's understandable as these are required fields.
|
||||
The error messages are hidden because the form is pristine; we haven't changed anything yet.
|
||||
The error messages are hidden because the form is pristine; you haven't changed anything yet.
|
||||
|
||||
Enter a name and click *New Hero* again.
|
||||
The app displays a **_Name is required_** error message!
|
||||
We don't want error messages when we create a new (empty) hero.
|
||||
Why are we getting one now?
|
||||
The app displays a _Name is required_ error message.
|
||||
You don't want error messages when you create a new (empty) hero.
|
||||
Why are you getting one now?
|
||||
|
||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
||||
The form remembers that we entered a name before clicking *New Hero*.
|
||||
The form remembers that you entered a name before clicking *New Hero*.
|
||||
Replacing the hero object *did not restore the pristine state* of the form controls.
|
||||
|
||||
We have to clear all of the flags imperatively which we can do
|
||||
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'}
|
||||
|
||||
Now clicking "New Hero" both resets the form and its control flags.
|
||||
Now clicking "New Hero" resets both the form and its control flags.
|
||||
|
||||
## Submit the form with _ngSubmit_
|
||||
|
||||
The user should be able to submit this form after filling it in.
|
||||
The Submit button at the bottom of the form
|
||||
The *Submit* button at the bottom of the form
|
||||
does nothing on its own, but it will
|
||||
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:
|
||||
We slipped in something extra there at the end! We defined a
|
||||
template reference variable, **`#heroForm`**, and initialized it with the value "ngForm".
|
||||
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.
|
||||
|
||||
### The _NgForm_ directive
|
||||
|
||||
What `NgForm` directive?
|
||||
We didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive!
|
||||
You didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive.
|
||||
|
||||
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
||||
|
||||
The `NgForm` directive supplements the `form` element with additional features.
|
||||
It holds the controls we created for the elements with an `ngModel` directive
|
||||
and `name` attribute, and monitors their properties including their validity.
|
||||
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.
|
||||
We'll bind the form's overall validity via
|
||||
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:
|
||||
If we run the application now, we find that the button is enabled
|
||||
— although it doesn't do anything useful yet.
|
||||
If you run the application now, you find that the button is enabled—although
|
||||
it doesn't do anything useful yet.
|
||||
|
||||
Now if we delete the Name, we violate the "required" rule, which
|
||||
Now if you delete the Name, you violate the "required" rule, which
|
||||
is duly noted in the error message.
|
||||
The Submit button is also disabled.
|
||||
The *Submit* button is also disabled.
|
||||
|
||||
Not impressed? Think about it for a moment. What would we have to do to
|
||||
Not impressed? Think about it for a moment. What would you have to do to
|
||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
||||
|
||||
For us, it was as simple as:
|
||||
For you, it was as simple as this:
|
||||
|
||||
1. Define a template reference variable on the (enhanced) form element.
|
||||
2. Refer to that variable in a button many lines away.
|
||||
|
@ -575,49 +572,47 @@ For us, it was as simple as:
|
|||
Submitting the form isn't terribly dramatic at the moment.
|
||||
|
||||
An unsurprising observation for a demo. To be honest,
|
||||
jazzing it up won't teach us anything new about forms.
|
||||
But this is an opportunity to exercise some of our newly won
|
||||
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, go ahead and skip to this guide's conclusion.
|
||||
Let's do something more strikingly visual.
|
||||
Let's hide the data entry area and display something else.
|
||||
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.
|
||||
|
||||
Start by wrapping the form in a `<div>` and bind
|
||||
Wrap the form in a `<div>` and bind
|
||||
its `hidden` property to the `HeroFormComponent.submitted` property.
|
||||
The main form is visible from the start because the
|
||||
`submitted` property is false until we submit the form,
|
||||
`submitted` property is false until you submit the form,
|
||||
as this fragment from the `HeroFormComponent` shows:
|
||||
When we click the Submit button, the `submitted` flag becomes true and the form disappears
|
||||
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 we just wrote:
|
||||
There's our hero again, displayed read-only with interpolation bindings.
|
||||
Add the following HTML below the `<div>` wrapper you just wrote:
|
||||
There's the hero again, displayed read-only with interpolation bindings.
|
||||
This `<div>` appears only while the component is in the submitted state.
|
||||
|
||||
The HTML includes an _Edit_ button whose click event is bound to an expression
|
||||
The HTML includes an *Edit* button whose click event is bound to an expression
|
||||
that clears the `submitted` flag.
|
||||
|
||||
When we click the _Edit_ button, this block disappears and the editable form reappears.
|
||||
|
||||
That's as much drama as we can muster for now.
|
||||
When you click the *Edit* button, this block disappears and the editable form reappears.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The Angular form discussed in this guide takes advantage of the following
|
||||
The Angular form discussed in this page takes advantage of the following
|
||||
framework features to provide support for data modification, validation, and more:
|
||||
|
||||
- An Angular HTML form template.
|
||||
- A form component class with a `@Component` decorator.
|
||||
- Handling form submission by binding to the `NgForm.ngSubmit` event property.
|
||||
- Template reference variables such as `#heroForm` and `#name`.
|
||||
- Template-reference variables such as `#heroForm` and `#name`.
|
||||
- `[(ngModel)]` syntax for two-way data binding.
|
||||
- The use of `name` attributes for validation and form element change tracking.
|
||||
- The use of `name` attributes for validation and form-element change tracking.
|
||||
- The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
||||
- Controlling the submit button's enabled state by binding to `NgForm` validity.
|
||||
- Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
|
||||
- Custom CSS classes that provide visual feedback to users about invalid controls.
|
||||
|
||||
Our final project folder structure should look like this:
|
||||
The final project folder structure should look like this:
|
||||
|
||||
<aio-filetree>
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ The following diagram represents the state of the this guide's three-level compo
|
|||
open simultaneously.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"> </img>
|
||||
<img src="assets/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"> </img>
|
||||
</figure>
|
||||
|
||||
### Injector bubbling
|
||||
|
@ -109,7 +109,7 @@ Each tax return component
|
|||
* has the ability to save the changes to its tax return or cancel them.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"> </img>
|
||||
<img src="assets/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"> </img>
|
||||
</figure>
|
||||
|
||||
One might suppose that the `TaxReturnComponent` has logic to manage and restore changes.
|
||||
|
@ -166,7 +166,7 @@ that have special capabilites suitable for whatever is going on in component (B)
|
|||
Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/dependency-injection/car-components.png" alt="car components" width="220"> </img>
|
||||
<img src="assets/images/devguide/dependency-injection/car-components.png" alt="car components" width="220"> </img>
|
||||
</figure>
|
||||
|
||||
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
|
||||
|
@ -176,7 +176,7 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
|||
`Tires` resolved by the root injector (A).
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"> </img>
|
||||
<img src="assets/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -132,11 +132,14 @@ Each page includes code snippets from a sample application that accompanies the
|
|||
You can reuse these snippets in your applications.
|
||||
|
||||
Look for a link to a running version of that sample, often near the top of the page,
|
||||
such as this <live-example name="architecture"></live-example> from the [Architecture](architecture.html) page.
|
||||
such as this <live-example nodownload name="architecture"></live-example> from the [Architecture](architecture.html) page.
|
||||
<span if-docs="ts">
|
||||
The link launches a browser-based, code editor where you can inspect, modify, save, and download the code.
|
||||
</span>
|
||||
|
||||
Alternatively, you can run the example locally, next to those `live-example` links you have a <a href="/resources/zips/architecture/architecture.zip">download link</a>.
|
||||
Just download, unzip, run `npm install` to install the dependencies and run it with `npm start`.
|
||||
|
||||
## Reference pages
|
||||
|
||||
* The [Cheat Sheet](cheatsheet.html) lists Angular syntax for common scenarios.
|
||||
|
|
|
@ -7,7 +7,7 @@ A suggested path through the documentation for Angular newcomers
|
|||
@description
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"> </img>
|
||||
<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>
|
||||
|
||||
Everyone learns differently.
|
||||
|
@ -17,7 +17,7 @@ Those new to Angular may wish to follow this popular learning path.
|
|||
|
||||
1. [Setup](setup.html "Setup locally withe Quickstart seed") for local Angular development, if you haven't already done so.
|
||||
|
||||
1. Take the [*Tour of Heroes* tutorial](../tutorial "Tour of Heroes").
|
||||
1. Take the [*Tour of Heroes* tutorial](../tutorial "Tour of Heroes").
|
||||
|
||||
The *Tour of Heroes* takes you step-by-step from [setup](setup.html)
|
||||
to a full-featured example that demonstrates the essential characteristics of a professional application:
|
||||
|
@ -42,5 +42,5 @@ After reading the above sections, feel free to skip around among the other pages
|
|||
|
||||
### Next Step
|
||||
|
||||
Try the [tutorial](../tutorial "Tour of Heroes") if you're ready to start coding or
|
||||
Try the [tutorial](../tutorial "Tour of Heroes") if you're ready to start coding or
|
||||
visit the [Architecture](architecture.html "Basic Concepts") page if you prefer to learn the basic concepts first.
|
|
@ -7,7 +7,7 @@ Angular calls lifecycle hook methods on directives and components as it creates,
|
|||
@description
|
||||
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"> </img>
|
||||
<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.
|
||||
|
@ -416,7 +416,7 @@ The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
|||
|
||||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"> </img>
|
||||
<img src="assets/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"> </img>
|
||||
</figure>
|
||||
|
||||
The sequence of log messages follows the prescribed hook calling order:
|
||||
|
@ -465,7 +465,7 @@ 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:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"> </img>
|
||||
<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.
|
||||
|
@ -538,7 +538,7 @@ The host `OnChangesParentComponent` binds to them like this:
|
|||
Here's the sample in action as the user makes changes.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img>
|
||||
<img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img>
|
||||
</figure>
|
||||
|
||||
The log entries appear as the string value of the *power* property changes.
|
||||
|
@ -561,7 +561,7 @@ It writes a special message to the log when there are no substantive changes to
|
|||
so you can see how often `DoCheck` is called. The results are illuminating:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"> </img>
|
||||
<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.
|
||||
|
@ -611,7 +611,7 @@ 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
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"> </img>
|
||||
<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.
|
||||
|
@ -644,7 +644,7 @@ The `<ng-content>` tag is a *placeholder* for the external content.
|
|||
It tells Angular where to insert that content.
|
||||
In this case, the projected content is the `<my-child>` from the parent.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"> </img>
|
||||
<img src='assets/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -181,7 +181,7 @@ Now enter the `@Component` decorator that specifies the `HeroDetailComponent` me
|
|||
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='metadata'}
|
||||
|
||||
The `moduleId: module.id` lets you use
|
||||
[component-relative paths](./cookbook/component-relative-paths.html) in file URLs
|
||||
[component-relative paths](../cookbook/component-relative-paths.html) in file URLs
|
||||
such as when specifying the `templateUrl`.
|
||||
|
||||
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
|
||||
|
@ -285,7 +285,7 @@ Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
|
|||
Now that everything is wired up, the browser should display something like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/just-formcontrol.png" width="400px" alt="Single FormControl"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/just-formcontrol.png" width="400px" alt="Single FormControl"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -357,7 +357,7 @@ The `heroForm.value` returns the _form model_.
|
|||
Piping it through the `JsonPipe` renders the model as JSON in the browser:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/json-output.png" width="400px" alt="JSON output"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/json-output.png" width="400px" alt="JSON output"> </img>
|
||||
</figure>
|
||||
|
||||
The initial `name` property value is the empty string.
|
||||
|
@ -429,7 +429,7 @@ Configuring validation is harder in template-driven forms where you must wrap va
|
|||
The browser displays the following:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/validators-json-output.png" width="400px" alt="Single FormControl"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/validators-json-output.png" width="400px" alt="Single FormControl"> </img>
|
||||
</figure>
|
||||
|
||||
`Validators.required` is working. The status is `INVALID` because the input box has no value.
|
||||
|
@ -527,7 +527,7 @@ After these changes, the JSON output in the browser shows the revised _form mode
|
|||
with the nested address `FormGroup`:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/address-group.png" width="400px" alt="JSON output"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/address-group.png" width="400px" alt="JSON output"> </img>
|
||||
</figure>
|
||||
|
||||
Great! You’ve made a group and you can see that the template
|
||||
|
@ -712,9 +712,9 @@ Take a moment to refactor the _address_ `FormGroup` definition for brevity and c
|
|||
|
||||
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='address-form-group'}
|
||||
|
||||
Also be sure to update the import from `data-model` so you can reference the `Hero` class:
|
||||
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-6.component.ts' region='import-hero'}
|
||||
{@example 'reactive-forms/ts/src/app/hero-detail-7.component.ts' region='import-address'}
|
||||
|
||||
|
||||
|
||||
|
@ -819,7 +819,7 @@ The `HeroDetailComponent` is a nested sub-component of the `HeroListComponent` i
|
|||
Together they look a bit like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/hero-list.png" width="420px" alt="HeroListComponent"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/hero-list.png" width="420px" alt="HeroListComponent"> </img>
|
||||
</figure>
|
||||
|
||||
The `HeroListComponent` uses an injected `HeroService` to retrieve heroes from the server
|
||||
|
@ -844,8 +844,8 @@ The techniques involved are covered elsewhere in the documentation, including th
|
|||
If you're coding along with the steps in this reactive forms tutorial,
|
||||
create the pertinent files based on the
|
||||
[source code displayed below](#source-code "Reactive Forms source code").
|
||||
Notice that `hero-list.component.ts` and `hero-list.component.ts`
|
||||
import `Observable` and `finally` from `rxjs`.
|
||||
Notice that `hero-list.component.ts` imports `Observable` and `finally` while `hero.service.ts` imports `Observable`, `of`,
|
||||
and `delay` from `rxjs`.
|
||||
Then return here to learn about _form array_ properties.
|
||||
|
||||
|
||||
|
@ -987,7 +987,7 @@ Back in the browser, select the hero named "Magneta".
|
|||
"Magneta" doesn't have an address, as you can see in the diagnostic JSON at the bottom of the form.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array"> </img>
|
||||
</figure>
|
||||
|
||||
Click the "_Add a Secret Lair_" button.
|
||||
|
@ -1046,7 +1046,7 @@ In a real app, you'd also be able to revert unsaved changes and resume editing.
|
|||
After you implement both features in this section, the form will look like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons"> </img>
|
||||
<img src="assets/images/devguide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons"> </img>
|
||||
</figure>
|
||||
|
||||
### Save
|
||||
|
|
|
@ -411,12 +411,12 @@ Once the app warms up, you'll see a row of navigation buttons
|
|||
and the *Heroes* view with its list of heroes.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/hero-list.png' alt="Hero List" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/hero-list.png' alt="Hero List" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
Select one hero and the app takes you to a hero editing screen.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/hero-detail.png' alt="Crisis Center Detail" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/hero-detail.png' alt="Crisis Center Detail" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
Alter the name.
|
||||
|
@ -430,7 +430,7 @@ Angular app navigation updates the browser history as normal web navigation does
|
|||
Now click the *Crisis Center* link for a list of ongoing crises.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/crisis-center-list.png' alt="Crisis Center List" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/crisis-center-list.png' alt="Crisis Center List" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
Select a crisis and the application takes you to a crisis editing screen.
|
||||
|
@ -440,7 +440,7 @@ Alter the name of a crisis.
|
|||
Notice that the corresponding name in the crisis list does _not_ change.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/crisis-center-detail.png' alt="Crisis Center Detail" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/crisis-center-detail.png' alt="Crisis Center Detail" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
Unlike *Hero Detail*, which updates as you type,
|
||||
|
@ -453,7 +453,7 @@ Click the browser back button or the "Heroes" link instead.
|
|||
Up pops a dialog box.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/confirm-dialog.png' alt="Confirm Dialog" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/confirm-dialog.png' alt="Confirm Dialog" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
You can say "OK" and lose your changes or click "Cancel" and continue editing.
|
||||
|
@ -470,7 +470,7 @@ Proceed to the first application milestone.
|
|||
|
||||
Begin with a simple version of the app that navigates between two empty views.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/router-1-anim.gif' alt="App in action" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -575,7 +575,7 @@ The root `AppComponent` is the application shell. It has a title, a navigation b
|
|||
and a *router outlet* where the router swaps views on and off the page. Here's what you get:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/shell-and-outlet.png' alt="Shell" width="300"> </img>
|
||||
<img src='assets/images/devguide/router/shell-and-outlet.png' alt="Shell" width="300"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -930,7 +930,7 @@ from the <live-example name="toh-4" title="Tour of Heroes: Services example code
|
|||
Here's how the user will experience this version of the app:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/router-2-anim.gif' alt="App in action"> </img>
|
||||
<img src='assets/images/devguide/router/router-2-anim.gif' alt="App in action"> </img>
|
||||
</figure>
|
||||
|
||||
A typical application has multiple *feature areas*,
|
||||
|
@ -1325,7 +1325,7 @@ For example, when returning to the heroes list from the hero detail view,
|
|||
it would be nice if the viewed hero was preselected in the list.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/selected-hero.png' alt="Selected hero"> </img>
|
||||
<img src='assets/images/devguide/router/selected-hero.png' alt="Selected hero"> </img>
|
||||
</figure>
|
||||
|
||||
You'll implement this feature in a moment by including the viewed hero's `id`
|
||||
|
@ -1422,7 +1422,7 @@ The binding adds the `selected` CSS class when the method returns `true` and rem
|
|||
Look for it within the repeated `<li>` tag as shown here:
|
||||
When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected:
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/selected-hero.png' alt="Selected List"> </img>
|
||||
<img src='assets/images/devguide/router/selected-hero.png' alt="Selected List"> </img>
|
||||
</figure>
|
||||
|
||||
The optional `foo` route parameter is harmless and continues to be ignored.
|
||||
|
@ -1662,7 +1662,7 @@ You'll organize the crisis center to conform to the following recommended patter
|
|||
If your app had many feature areas, the app component trees might look like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/component-tree.png' alt="Component Tree"> </img>
|
||||
<img src='assets/images/devguide/router/component-tree.png' alt="Component Tree"> </img>
|
||||
</figure>
|
||||
|
||||
### Child routing component
|
||||
|
@ -1830,7 +1830,7 @@ It displays a simple form with a header, an input box for the message,
|
|||
and two buttons, "Send" and "Cancel".
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/router/contact-popup.png' alt="Contact popup" width="250"> </img>
|
||||
<img src='assets/images/devguide/router/contact-popup.png' alt="Contact popup" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
Here's the component and its template:
|
||||
|
@ -2507,7 +2507,7 @@ accessing the admin feature area.
|
|||
It redirects to the login page if the user is not authorized.
|
||||
|
||||
But the router is still loading the `AdminModule` even if the user can't visit any of its components.
|
||||
Ideally, you's only load the `AdminModule` if the user is logged in.
|
||||
Ideally, you'd only load the `AdminModule` if the user is logged in.
|
||||
|
||||
Add a **`CanLoad`** guard that only loads the `AdminModule` once the user is logged in _and_ attempts to access the admin feature area.
|
||||
|
||||
|
|
|
@ -5,21 +5,20 @@ Security
|
|||
Developing for content security in Angular applications
|
||||
|
||||
@description
|
||||
This section describes Angular's built-in
|
||||
protections against common web application vulnerabilities and attacks such as cross-site
|
||||
scripting attacks. It does not cover application-level security, such as authentication (_Who is
|
||||
this user?_) or authorization (_What can this user do?_).
|
||||
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
|
||||
this user?_) and authorization (_What can this user do?_).
|
||||
|
||||
For more information about the attacks and mitigations described below, see [OWASP Guide Project](https://www.owasp.org/index.php/Category:OWASP_Guide_Project).
|
||||
Try the <live-example></live-example> of the code shown in this page.
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
|
||||
<h2 id='report-issues'>
|
||||
Reporting vulnerabilities
|
||||
</h2>
|
||||
|
||||
Email us at [security@angular.io](mailto:security@angular.io) to report vulnerabilities in
|
||||
Angular itself.
|
||||
To report vulnerabilities in Angular itself, email us at [security@angular.io](mailto:security@angular.io).
|
||||
|
||||
For more information about how Google handles security issues, see [Google's security
|
||||
philosophy](https://www.google.com/about/appsecurity/).
|
||||
|
@ -30,7 +29,7 @@ philosophy](https://www.google.com/about/appsecurity/).
|
|||
</h2>
|
||||
|
||||
* **Keep current with the latest Angular library releases.**
|
||||
We regularly update our Angular libraries, and these updates may fix security defects discovered in
|
||||
We regularly update the Angular libraries, and these updates may fix security defects discovered in
|
||||
previous versions. Check the Angular [change
|
||||
log](https://github.com/angular/angular/blob/master/CHANGELOG.md) for security-related updates.
|
||||
|
||||
|
@ -39,7 +38,8 @@ Private, customized versions of Angular tend to fall behind the current version
|
|||
important security fixes and enhancements. Instead, share your Angular improvements with the
|
||||
community and make a pull request.
|
||||
|
||||
* **Avoid Angular APIs marked in the documentation as “[_Security Risk_](#bypass-security-apis).”**
|
||||
* **Avoid Angular APIs marked in the documentation as “_Security Risk_.”**
|
||||
For more information, see the [Trusting safe values](#bypass-security-apis) section of this page.
|
||||
|
||||
|
||||
<h2 id='xss'>
|
||||
|
@ -48,79 +48,83 @@ community and make a pull request.
|
|||
|
||||
[Cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers
|
||||
to inject malicious code into web pages. Such code can then, for example, steal user data (in
|
||||
particular, their login data) or perform actions impersonating the user. This is one of the most
|
||||
particular, login data) or perform actions to impersonate the user. This is one of the most
|
||||
common attacks on the web.
|
||||
|
||||
To block XSS attacks, you must prevent malicious code from entering the DOM (Document Object Model). For example, if an
|
||||
attacker can trick you into inserting a `<script>` tag in the DOM, they can run arbitrary code on
|
||||
your website. The attack is not limited to `<script>` tags—many elements and properties in the
|
||||
To block XSS attacks, you must prevent malicious code from entering the DOM (Document Object Model). For example, if
|
||||
attackers can trick you into inserting a `<script>` tag in the DOM, they can run arbitrary code on
|
||||
your website. The attack isn't limited to `<script>` tags—many elements and properties in the
|
||||
DOM allow code execution, for example, `<img onerror="...">` and `<a href="javascript:...">`. If
|
||||
attacker-controlled data enters the DOM, expect security vulnerabilities.
|
||||
|
||||
### Angular’s cross-site scripting security model
|
||||
|
||||
To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value
|
||||
is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values.
|
||||
is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation,
|
||||
Angular sanitizes and escapes untrusted values.
|
||||
|
||||
_Angular templates are the same as executable code_: HTML, attributes, and binding expressions
|
||||
(but not the values bound!) in templates are trusted to be safe. This means that applications must
|
||||
(but not the values bound) in templates are trusted to be safe. This means that applications must
|
||||
prevent values that an attacker can control from ever making it into the source code of a
|
||||
template. Never generate template source code by concatenating user input and templates! Using
|
||||
the [offline template compiler](#offline-template-compiler) is an effective way to prevent these
|
||||
vulnerabilities, also known as _template injection_.
|
||||
template. Never generate template source code by concatenating user input and templates.
|
||||
To prevent these vulnerabilities, use
|
||||
the [offline template compiler](#offline-template-compiler), also known as _template injection_.
|
||||
|
||||
### Sanitization and security contexts
|
||||
|
||||
_Sanitization_ is the inspection of an untrusted value, turning it into a value that is safe to insert into
|
||||
the DOM. In many cases, sanitization does not change a value at all. Sanitization depends on context:
|
||||
a value that is harmless in CSS is potentially dangerous in a URL.
|
||||
_Sanitization_ is the inspection of an untrusted value, turning it into a value that's safe to insert into
|
||||
the DOM. In many cases, sanitization doesn't change a value at all. Sanitization depends on context:
|
||||
a value that's harmless in CSS is potentially dangerous in a URL.
|
||||
|
||||
Angular defines four security contexts—HTML, style, URL, and resource URL:
|
||||
Angular defines the following security contexts:
|
||||
|
||||
* **HTML** is used when interpreting a value as HTML, for example, when binding to `innerHtml`
|
||||
* **Style** is used when binding CSS into the `style` property
|
||||
* **URL** is used for URL properties such as `<a href>`
|
||||
* **Resource URL** is a URL that will be loaded and executed as code, for example, in `<script src>`
|
||||
* **HTML** is used when interpreting a value as HTML, for example, when binding to `innerHtml`.
|
||||
* **Style** is used when binding CSS into the `style` property.
|
||||
* **URL** is used for URL properties, such as `<a href>`.
|
||||
* **Resource URL** is a URL that will be loaded and executed as code, for example, in `<script src>`.
|
||||
|
||||
Angular sanitizes untrusted values for the first three items; sanitizing resource URLs is not
|
||||
Angular sanitizes untrusted values for HTML, styles, and URLs; sanitizing resource URLs isn't
|
||||
possible because they contain arbitrary code. In development mode, Angular prints a console warning
|
||||
when it has to change a value during sanitization.
|
||||
|
||||
### Sanitization example
|
||||
|
||||
The template below binds the value of `htmlSnippet`, once by interpolating it into an element's
|
||||
The following template binds the value of `htmlSnippet`, once by interpolating it into an element's
|
||||
content, and once by binding it to the `innerHTML` property of an element:
|
||||
|
||||
|
||||
{@example 'security/ts/src/app/inner-html-binding.component.html'}
|
||||
|
||||
Interpolated content is always escaped—the HTML is not interpreted, and the browser displays
|
||||
Interpolated content is always escaped—the HTML isn't interpreted and the browser displays
|
||||
angle brackets in the element's text content.
|
||||
|
||||
For the HTML to be interpreted, you must bind it to an HTML property such as `innerHTML`. But binding
|
||||
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:
|
||||
### Avoid direct use of the DOM APIs
|
||||
|
||||
The built-in browser DOM APIs do not automatically protect you from security vulnerabilities.
|
||||
The built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
|
||||
For example, `document`, the node available through `ElementRef`, and many third-party APIs
|
||||
contain unsafe methods. Avoid directly interacting with the DOM and instead use Angular
|
||||
templates where possible.
|
||||
|
||||
### Content security policy
|
||||
|
||||
[Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
||||
Content Security Policy (CSP) is a defense-in-depth
|
||||
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
|
||||
`Content-Security-Policy` HTTP header.
|
||||
`Content-Security-Policy` HTTP header. Read more about content security policy at
|
||||
[An Introduction to Content Security Policy](http://www.html5rocks.com/en/tutorials/security/content-security-policy/)
|
||||
on the HTML5Rocks website.
|
||||
|
||||
<a id="offline-template-compiler"></a>
|
||||
### Use the offline template compiler
|
||||
|
||||
The offline template compiler prevents a whole class of vulnerabilities called template injection,
|
||||
and also greatly improves application performance. Use the offline template compiler in production
|
||||
deployments; do not dynamically generate templates. Angular trusts template code, so generating
|
||||
templates, in particular templates containing user data, circumvents Angular's built-in protections. For information about how to dynamically construct forms in a safe way, see
|
||||
[Dynamic Forms Cookbook](../cookbook/dynamic-form.html).
|
||||
and greatly improves application performance. Use the offline template compiler in production
|
||||
deployments; don't dynamically generate templates. Angular trusts template code, so generating
|
||||
templates, in particular templates containing user data, circumvents Angular's built-in protections.
|
||||
For information about dynamically constructing forms in a safe way, see the
|
||||
[Dynamic Forms](../cookbook/dynamic-form.html) cookbook page.
|
||||
|
||||
### Server-side XSS protection
|
||||
|
||||
|
@ -128,12 +132,12 @@ HTML constructed on the server is vulnerable to injection attacks. Injecting tem
|
|||
Angular application is the same as injecting executable code into the
|
||||
application: it gives the attacker full control over the application. To prevent this,
|
||||
use a templating language that automatically escapes values to prevent XSS vulnerabilities on
|
||||
the server. Do not generate Angular templates on the server side using a templating language; doing this
|
||||
the server. Don't generate Angular templates on the server side using a templating language; doing this
|
||||
carries a high risk of introducing template-injection vulnerabilities.
|
||||
|
||||
|
||||
<h2 id='code-review'>
|
||||
Auditing angular applications
|
||||
Auditing Angular applications
|
||||
</h2>
|
||||
|
||||
Angular applications must follow the same security principles as regular web applications, and
|
||||
|
|
|
@ -69,7 +69,7 @@ The app uses the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR
|
|||
|
||||
It works like this:
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250"> </img>
|
||||
<img src='assets/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
This demo has a single component, the `HeroListComponent`. Here's its template:
|
||||
|
@ -279,7 +279,7 @@ Here is a simple search that shows suggestions from Wikipedia as the user
|
|||
types in a text box:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250"> </img>
|
||||
<img src='assets/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ The `ngIf` directive doesn't hide elements with CSS. It adds and removes them ph
|
|||
Confirm that fact using browser developer tools to inspect the DOM.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM"> </img>
|
||||
</figure>
|
||||
|
||||
The top paragraph is in the DOM. The bottom, disused paragraph is not;
|
||||
|
@ -139,7 +139,7 @@ A directive could hide the unwanted paragraph instead by setting its `display` s
|
|||
While invisible, the element remains in the DOM.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM"> </img>
|
||||
</figure>
|
||||
|
||||
The difference between hiding and removing doesn't matter for a simple paragraph.
|
||||
|
@ -201,7 +201,7 @@ You also have a CSS style rule that happens to apply to a `<span>` within a `<p>
|
|||
|
||||
The constructed paragraph renders strangely.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"> </img>
|
||||
</figure>
|
||||
|
||||
The `p span` style, intended for use elsewhere, was inadvertently applied here.
|
||||
|
@ -216,7 +216,7 @@ When you try this,
|
|||
|
||||
the drop down is empty.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work"> </img>
|
||||
</figure>
|
||||
|
||||
The browser won't display an `<option>` within a `<span>`.
|
||||
|
@ -232,7 +232,7 @@ Here's the conditional paragraph again, this time using `<ng-container>`.
|
|||
|
||||
It renders properly.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"> </img>
|
||||
</figure>
|
||||
|
||||
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
|
@ -241,7 +241,7 @@ Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
|||
|
||||
The drop down works properly.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"> </img>
|
||||
</figure>
|
||||
|
||||
The `<ng-container>` is a syntax element recognized by the Angular parser.
|
||||
|
@ -289,7 +289,7 @@ Then it translates the template _attribute_ into a template _element_, wrapped a
|
|||
None of these forms are actually rendered.
|
||||
Only the finished product ends up in the DOM.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"> </img>
|
||||
</figure>
|
||||
|
||||
Angular consumed the `<template>` content during its actual rendering and
|
||||
|
@ -390,7 +390,7 @@ variable as the `hero` declared as `#hero`.
|
|||
{@a one-per-element}
|
||||
### One structural directive per host element
|
||||
|
||||
Someday you'll want to to repeat a block of HTML but only when a particular condition is true.
|
||||
Someday you'll want to repeat a block of HTML but only when a particular condition is true.
|
||||
You'll _try_ to put both an `*ngFor` and an `*ngIf` on the same host element.
|
||||
Angular won't let you. You may apply only one _structural_ directive to an element.
|
||||
|
||||
|
@ -469,7 +469,7 @@ That's the fate of the middle "hip" in the phrase "Hip! Hip! Hooray!".
|
|||
|
||||
Angular erases the middle "hip", leaving the cheer a bit less enthusiastic.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"> </img>
|
||||
</figure>
|
||||
|
||||
A structural directive puts a `<template>` to work
|
||||
|
@ -554,7 +554,7 @@ Then create some HTML to try it.
|
|||
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.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"> </img>
|
||||
<img src='assets/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ The guideline will use the shortcut `hero.component.ts|html|css|spec` to represe
|
|||
## Single responsibility
|
||||
|
||||
Apply the
|
||||
<a href="https://wikipedia.org/wiki/Single_responsibility_principle" target="_blank"><i>Single Responsibility Principle</i> (SPR)</a>
|
||||
<a href="https://wikipedia.org/wiki/Single_responsibility_principle" target="_blank"><i>Single Responsibility Principle</i> (SRP)</a>
|
||||
to all components, services, and other symbols.
|
||||
This helps make the app cleaner, easier to read and maintain, and more testable.
|
||||
|
||||
|
@ -248,7 +248,7 @@ Naming conventions are hugely important to maintainability and readability. This
|
|||
|
||||
|
||||
<div class='s-why'>
|
||||
**Why?** The naming conventions should simply help find desited code faster and make it easier to understand.
|
||||
**Why?** The naming conventions should simply help find desired code faster and make it easier to understand.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -887,7 +887,7 @@ nor property binding.
|
|||
Interpolation handles the script tags differently than property binding but both approaches render the
|
||||
content harmlessly.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/template-syntax/evil-title.png' alt="evil title made safe" width='500px'> </img>
|
||||
<img src='assets/images/devguide/template-syntax/evil-title.png' alt="evil title made safe" width='500px'> </img>
|
||||
</figure>
|
||||
|
||||
<a href="#toc">back to top</a>
|
||||
|
@ -1353,7 +1353,7 @@ The following contrived example forces the input value to uppercase:
|
|||
|
||||
Here are all variations in action, including the uppercase version:
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/template-syntax/ng-model-anim.gif' alt="NgModel variations"> </img>
|
||||
<img src='assets/images/devguide/template-syntax/ng-model-anim.gif' alt="NgModel variations"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1555,7 +1555,7 @@ Here is an illustration of the _trackBy_ effect.
|
|||
* With `trackBy`, only changing the `id` triggers element replacement.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/template-syntax/ng-for-track-by-anim.gif' alt="trackBy"> </img>
|
||||
<img src='assets/images/devguide/template-syntax/ng-for-track-by-anim.gif' alt="trackBy"> </img>
|
||||
</figure>
|
||||
|
||||
<a href="#toc">back to top</a>
|
||||
|
@ -1579,7 +1579,7 @@ Angular puts only the *selected* element into the DOM.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/template-syntax/switch-anim.gif' alt="trackBy"> </img>
|
||||
<img src='assets/images/devguide/template-syntax/switch-anim.gif' alt="trackBy"> </img>
|
||||
</figure>
|
||||
|
||||
`NgSwitch` is the controller directive. Bind it to an expression that returns the *switch value*.
|
||||
|
@ -1753,7 +1753,7 @@ Don't do both!### Input or output?
|
|||
|
||||
The terms _input_ and _output_ reflect the perspective of the target directive.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/template-syntax/input-output.png' alt="Inputs and outputs"> </img>
|
||||
<img src='assets/images/devguide/template-syntax/input-output.png' alt="Inputs and outputs"> </img>
|
||||
</figure>
|
||||
|
||||
`HeroDetailComponent.hero` is an **input** property from the perspective of `HeroDetailComponent`
|
||||
|
|
|
@ -309,7 +309,7 @@ everything work seamlessly:
|
|||
use in AngularJS.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/upgrade/injectors.png" alt="The two injectors in a hybrid application" width="700"> </img>
|
||||
<img src="assets/images/devguide/upgrade/injectors.png" alt="The two injectors in a hybrid application" width="700"> </img>
|
||||
</figure>
|
||||
|
||||
#### Components and the DOM
|
||||
|
@ -349,7 +349,7 @@ ways:
|
|||
and Angular content projection together.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/upgrade/dom.png" alt="DOM element ownership in a hybrid application" width="500"> </img>
|
||||
<img src="assets/images/devguide/upgrade/dom.png" alt="DOM element ownership in a hybrid application" width="500"> </img>
|
||||
</figure>
|
||||
|
||||
Whenever we use a component that belongs to the other framework, a
|
||||
|
@ -395,7 +395,7 @@ AngularJS and Angular approaches. Here's what happens:
|
|||
detection after every event.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="/resources/images/devguide/upgrade/change_detection.png" alt="Change detection in a hybrid application" width="600"> </img>
|
||||
<img src="assets/images/devguide/upgrade/change_detection.png" alt="Change detection in a hybrid application" width="600"> </img>
|
||||
</figure>
|
||||
|
||||
What this means in practice is that we do not need to call `$apply()` in
|
||||
|
@ -491,7 +491,7 @@ Congratulations! You're running a hybrid application! The
|
|||
existing AngularJS code works as before _and_ you're ready to run Angular code.
|
||||
### Using Angular Components from AngularJS Code
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/upgrade/ajs-to-a.png" alt="Using an Angular component from AngularJS code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/upgrade/ajs-to-a.png" alt="Using an Angular component from AngularJS code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Once we're running a hybrid app, we can start the gradual process of upgrading
|
||||
|
@ -523,7 +523,7 @@ Angular module.
|
|||
|
||||
|
||||
All Angular components, directives and pipes must be declared in an NgModule.
|
||||
The net resulit is an AngularJS directive called `heroDetail`, that we can
|
||||
The net result is an AngularJS directive called `heroDetail`, that we can
|
||||
use like any other directive in our AngularJS templates.
|
||||
|
||||
|
||||
|
@ -602,7 +602,7 @@ For example, we can easily make multiple copies of the component using `ng-repe
|
|||
|
||||
### Using AngularJS Component Directives from Angular Code
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/upgrade/a-to-ajs.png" alt="Using an AngularJS component from Angular code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/upgrade/a-to-ajs.png" alt="Using an AngularJS component from Angular code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
So, we can write an Angular component and then use it from AngularJS
|
||||
|
@ -781,7 +781,7 @@ and then provide the input and output using Angular template syntax:
|
|||
|
||||
### Projecting AngularJS Content into Angular Components
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/upgrade/ajs-to-a-with-projection.png" alt="Projecting AngularJS content into Angular" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/upgrade/ajs-to-a-with-projection.png" alt="Projecting AngularJS content into Angular" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
When we are using a downgraded Angular component from an AngularJS
|
||||
|
@ -815,7 +815,7 @@ remains in "AngularJS land" and is managed by the AngularJS framework.
|
|||
|
||||
### Transcluding Angular Content into AngularJS Component Directives
|
||||
<figure>
|
||||
<img src="/resources/images/devguide/upgrade/a-to-ajs-with-transclusion.png" alt="Projecting Angular content into AngularJS" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
<img src="assets/images/devguide/upgrade/a-to-ajs-with-transclusion.png" alt="Projecting Angular content into AngularJS" align="left" style="width:250px; margin-left:-40px;margin-right:10px"> </img>
|
||||
</figure>
|
||||
|
||||
Just like we can project AngularJS content into Angular components,
|
||||
|
@ -1657,7 +1657,7 @@ Now set the remaining `phone-detail.component.ts` as follows:
|
|||
This is similar to the phone list component.
|
||||
The new wrinkle is the `RouteParams` type annotation that identifies the `routeParams` dependency.
|
||||
|
||||
The AngularJS injector has an AngularJS router dependency called `$routeParams`.
|
||||
The AngularJS injector has an AngularJS router dependency called `$routeParams`,
|
||||
which was injected into `PhoneDetails` when it was still an AngularJS controller.
|
||||
We intend to inject it into the new `PhoneDetailsComponent`.
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ Here's what the UI displays:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/user-input/keyup1-anim.gif' alt="key up 1"> </img>
|
||||
<img src='assets/images/devguide/user-input/keyup1-anim.gif' alt="key up 1"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -134,7 +134,7 @@ and the component does nothing.
|
|||
Type something in the input box, and watch the display update with each keystroke.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back"> </img>
|
||||
<img src='assets/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -166,7 +166,7 @@ Then Angular calls the event handler only when the user presses _Enter_.
|
|||
|
||||
Here's how it works.
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/user-input/keyup3-anim.gif' alt="key up 3"> </img>
|
||||
<img src='assets/images/devguide/user-input/keyup3-anim.gif' alt="key up 3"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -193,7 +193,7 @@ The user can add a hero by typing the hero's name in the input box and
|
|||
clicking **Add**.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"> </img>
|
||||
<img src='assets/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"> </img>
|
||||
</figure>
|
||||
|
||||
Below is the "Little Tour of Heroes" component.
|
||||
|
|
|
@ -38,7 +38,9 @@ This guide offers a taste of Webpack and explains how to use it with Angular app
|
|||
[Trying it out](#try)
|
||||
|
||||
[Conclusions](#conclusions)
|
||||
|
||||
|
||||
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.
|
||||
|
@ -156,7 +158,7 @@ Create a **new project folder**
|
|||
|
||||
</code-example>
|
||||
|
||||
Add these files to the root directory:
|
||||
Add these files:
|
||||
|
||||
<md-tab-group>
|
||||
|
||||
|
@ -165,8 +167,8 @@ Add these files to the root directory:
|
|||
</md-tab>
|
||||
|
||||
|
||||
<md-tab label="tsconfig.json">
|
||||
{@example 'webpack/ts/tsconfig.1.json'}
|
||||
<md-tab label="src/tsconfig.json">
|
||||
{@example 'webpack/ts/src/tsconfig.1.json'}
|
||||
</md-tab>
|
||||
|
||||
|
||||
|
@ -304,9 +306,9 @@ Rules tell Webpack which loaders to use for each file (AKA _module_):
|
|||
* 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 excludes `.css` files within the `/src/app` directories where the component-scoped styles sit.
|
||||
It includes only `.css` files located at or above `/src`; these are the application-wide styles.
|
||||
The `ExtractTextPlugin` (described below) applies the `style` and `css` loaders to these files.
|
||||
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 —
|
||||
which is what Angular expects to do with styles specified in a `styleUrls` metadata property.
|
||||
|
@ -484,8 +486,8 @@ Webpack techniques covered in this guide.
|
|||
</md-tab>
|
||||
|
||||
|
||||
<md-tab label="public/css/styles.css">
|
||||
{@example 'webpack/ts/public/css/styles.css'}
|
||||
<md-tab label="src/assets/css/styles.css">
|
||||
{@example 'webpack/ts/src/assets/css/styles.css'}
|
||||
</md-tab>
|
||||
|
||||
|
||||
|
@ -521,14 +523,10 @@ Webpack techniques covered in this guide.
|
|||
|
||||
</md-tab-group>
|
||||
|
||||
|
||||
<p>
|
||||
The <code>app.component.html</code> displays this downloadable Angular logo
|
||||
<a href="https://raw.githubusercontent.com/angular/angular.io/master/public/resources/images/logos/angular2/angular.png" target="_blank">
|
||||
<img src="/resources/images/logos/angular2/angular.png" height="40px" title="download Angular logo"></a>.
|
||||
|
||||
</p>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
{@a bundle-ts}
|
||||
|
|
|
@ -34,7 +34,7 @@ Here's a visual idea of where we're going in this tour, beginning with the "Dash
|
|||
view and our most heroic heroes:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard"> </img>
|
||||
<img src='assets/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard"> </img>
|
||||
</figure>
|
||||
|
||||
Above the dashboard are two links ("Dashboard" and "Heroes").
|
||||
|
@ -44,7 +44,7 @@ Instead we click the dashboard hero named "Magneta" and the router takes us to a
|
|||
of that hero where we can change the hero's name.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/hero-details-1.png' alt="Details of hero in app"> </img>
|
||||
<img src='assets/images/devguide/toh/hero-details-1.png' alt="Details of hero in app"> </img>
|
||||
</figure>
|
||||
|
||||
Clicking the "Back" button would return us to the "Dashboard".
|
||||
|
@ -52,7 +52,7 @@ Links at the top can take us to either of the main views.
|
|||
We'll click "Heroes". The app takes to the "Heroes" master list view.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app"> </img>
|
||||
<img src='assets/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app"> </img>
|
||||
</figure>
|
||||
|
||||
We click a different hero and the readonly mini-detail beneath the list reflects our new choice.
|
||||
|
@ -63,13 +63,13 @@ editable details of our selected hero.
|
|||
The following diagram captures all of our navigation options.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
|
||||
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
|
||||
</figure>
|
||||
|
||||
Here's our app in action
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action"> </img>
|
||||
<img src='assets/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ For example, when we select Magneta from the heroes list,
|
|||
we can make it pop out visually by giving it a subtle background color as shown here.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/heroes-list-selected.png' alt="Selected hero"> </img>
|
||||
<img src='assets/images/devguide/toh/heroes-list-selected.png' alt="Selected hero"> </img>
|
||||
</figure>
|
||||
|
||||
We’ll add a property binding on `class` for the `selected` class to the template. We'll set this to an expression that compares the current `selectedHero` to the `hero`.
|
||||
|
@ -329,7 +329,7 @@ The browser reloads our app.
|
|||
We select the hero Magneta and the selection is clearly identified by the background color.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app"> </img>
|
||||
<img src='assets/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app"> </img>
|
||||
</figure>
|
||||
|
||||
We select a different hero and the tell-tale color switches to that hero.
|
||||
|
|
|
@ -15,7 +15,7 @@ We received new requirements for our Tour of Heroes application:
|
|||
When we’re done, users will be able to navigate the app like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
|
||||
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
|
||||
</figure>
|
||||
|
||||
We'll add Angular’s *Router* to our app to satisfy these requirements.
|
||||
|
@ -93,6 +93,27 @@ Instead of displaying heroes automatically, we'd like to show them *after* the u
|
|||
In other words, we'd like to navigate to the list of heroes.
|
||||
|
||||
We'll need the Angular *Router*.
|
||||
### *<base href>*
|
||||
|
||||
Open `index.html` and ensure there is a `<base href="...">` element
|
||||
(or a script that dynamically sets this element)
|
||||
at the top of the `<head>` section.
|
||||
|
||||
|
||||
~~~ {.callout.is-important}
|
||||
|
||||
|
||||
<header>
|
||||
base href is essential
|
||||
</header>
|
||||
|
||||
See the *base href* section of the [router](../guide/router.html#base-href)
|
||||
guide to learn why this matters, and what to add if the `base`
|
||||
element is missing.
|
||||
|
||||
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
{@a configure-routes}
|
||||
|
@ -322,7 +343,7 @@ Although the dashboard heroes are presented as button-like blocks, they should b
|
|||
When hovering over a hero block, the target URL should display in the browser status bar
|
||||
and the user should be able to copy the link or open the hero detail view in a new tab.
|
||||
|
||||
To achieve this effect, reopen the `dashboard.component.html` and replace the repeated `<div *ngFor...>` tags
|
||||
To achieve this effect, reopen the <span ngio-ex>dashboard.component.html</span> and replace the repeated `<div *ngFor...>` tags
|
||||
with `<a>` tags. The opening `<a>` tag looks like this:
|
||||
|
||||
|
||||
|
@ -374,7 +395,7 @@ Add the following HTML fragment at the bottom of the template where the `<my-her
|
|||
After clicking a hero, the user should see something like this below the hero list:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/mini-hero-detail.png' alt="Mini Hero Detail" height="70"> </img>
|
||||
<img src='assets/images/devguide/toh/mini-hero-detail.png' alt="Mini Hero Detail" height="70"> </img>
|
||||
</figure>
|
||||
|
||||
### Format with the *uppercase* pipe
|
||||
|
@ -420,7 +441,7 @@ back in the `DashboardComponent`.
|
|||
Here's the fully revised `HeroesComponent` class:
|
||||
Refresh the browser and start clicking.
|
||||
We can navigate around the app, from the dashboard to hero details and back,
|
||||
for heroes list to the mini-detail to the hero details and back to the heroes again.
|
||||
from heroes list to the mini-detail to the hero details and back to the heroes again.
|
||||
We can jump back and forth between the dashboard and the heroes.
|
||||
|
||||
We've met all of the navigational requirements that propelled this chapter.
|
||||
|
@ -478,7 +499,7 @@ If necessary, also edit <span ngio-ex>index.html</span> to refer to this stylesh
|
|||
Look at the app now. Our dashboard, heroes, and navigation links are styling!
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations"> </img>
|
||||
<img src='assets/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ Run the app again, go to the *Dashboard*, and enter some text in the search box.
|
|||
At some point it might look like this.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='/resources/images/devguide/toh/toh-hero-search.png' alt="Hero Search Component"> </img>
|
||||
<img src='assets/images/devguide/toh/toh-hero-search.png' alt="Hero Search Component"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue