docs(aio): ensure blank lines before HTML blocks in markdown

This commit is contained in:
Peter Bacon Darwin 2017-03-30 20:04:18 +01:00 committed by Pete Bacon Darwin
parent 8ef621ad2a
commit 2d14c3b17a
58 changed files with 3788 additions and 83 deletions

View File

@ -24,6 +24,7 @@ You'll pursue these ends in the following high-level steps:
And you can also <a href="/resources/zips/cli-quickstart/cli-quickstart.zip">download the example.</a> And you can also <a href="/resources/zips/cli-quickstart/cli-quickstart.zip">download the example.</a>
<h2 id='devenv'> <h2 id='devenv'>
Step 1. Set up the Development Environment Step 1. Set up the Development Environment
</h2> </h2>
@ -43,6 +44,7 @@ Older versions produce errors, but newer versions are fine.
Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globally. Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globally.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm install -g @angular/cli npm install -g @angular/cli
@ -50,6 +52,7 @@ Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globa
<h2 id='create-project'> <h2 id='create-project'>
Step 2. Create a new project Step 2. Create a new project
</h2> </h2>
@ -57,6 +60,7 @@ Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globa
Open a terminal window. Open a terminal window.
Generate a new project and skeleton application by running the following commands: Generate a new project and skeleton application by running the following commands:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
ng new my-app ng new my-app
@ -74,12 +78,14 @@ It takes time to set up a new project, most of it spent installing npm packages.
<h2 id='serve'> <h2 id='serve'>
Step 3: Serve the application Step 3: Serve the application
</h2> </h2>
Go to the project directory and launch the server. Go to the project directory and launch the server.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
cd my-app cd my-app
ng serve --open ng serve --open
@ -94,12 +100,14 @@ on `http://localhost:4200/`.
Your app greets you with a message: Your app greets you with a message:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/app-works.png' alt="The app works!"> </img> <img src='assets/images/devguide/cli-quickstart/app-works.png' alt="The app works!"> </img>
</figure> </figure>
<h2 id='first-component'> <h2 id='first-component'>
Step 4: Edit your first Angular component Step 4: Edit your first Angular component
</h2> </h2>
@ -110,6 +118,7 @@ You can find it in `./src/app/app.component.ts`.
Open the component file and change the `title` property from _app works!_ to _My First Angular App_: Open the component file and change the `title` property from _app works!_ to _My First Angular App_:
<code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false"> <code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false">
</code-example> </code-example>
@ -119,11 +128,13 @@ The browser reloads automatically with the revised title. That's nice, but it co
Open `src/app/app.component.css` and give the component some style. Open `src/app/app.component.css` and give the component some style.
<code-example path="cli-quickstart/src/app/app.component.css" linenums="false"> <code-example path="cli-quickstart/src/app/app.component.css" linenums="false">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"> </img> <img src='assets/images/devguide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"> </img>
</figure> </figure>
@ -155,32 +166,40 @@ Your app lives in the `src` folder.
All Angular components, templates, styles, images, and anything else your app needs go here. All Angular components, templates, styles, images, and anything else your app needs go here.
Any files outside of this folder are meant to support building your app. Any files outside of this folder are meant to support building your app.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.css app.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.html app.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.spec.ts app.component.spec.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
@ -189,8 +208,10 @@ Any files outside of this folder are meant to support building your app.
</aio-folder> </aio-folder>
<aio-folder> <aio-folder>
assets assets
<aio-file> <aio-file>
.gitkeep .gitkeep
</aio-file> </aio-file>
@ -199,13 +220,16 @@ Any files outside of this folder are meant to support building your app.
</aio-folder> </aio-folder>
<aio-folder> <aio-folder>
environments environments
<aio-file> <aio-file>
environment.prod.ts environment.prod.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
environment.ts environment.ts
</aio-file> </aio-file>
@ -214,41 +238,49 @@ Any files outside of this folder are meant to support building your app.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
favicon.ico favicon.ico
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
polyfills.ts polyfills.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
test.ts test.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.app.json tsconfig.app.json
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.spec.json tsconfig.spec.json
</aio-file> </aio-file>
@ -260,30 +292,37 @@ Any files outside of this folder are meant to support building your app.
</aio-filetree> </aio-filetree>
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
</style> </style>
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
@ -292,13 +331,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>app/app.component.{ts,html,css,spec.ts}</code> <code>app/app.component.{ts,html,css,spec.ts}</code>
</td> </td>
<td> <td>
Defines the `AppComponent` along with an HTML template, CSS stylesheet, and a unit test. Defines the `AppComponent` along with an HTML template, CSS stylesheet, and a unit test.
It is the **root** component of what will become a tree of nested components It is the **root** component of what will become a tree of nested components
@ -309,13 +351,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>app/app.module.ts</code> <code>app/app.module.ts</code>
</td> </td>
<td> <td>
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application. Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`. Right now it declares only the `AppComponent`.
@ -326,13 +371,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>assets/*</code> <code>assets/*</code>
</td> </td>
<td> <td>
A folder where you can put images and anything else to be copied wholesale A folder where you can put images and anything else to be copied wholesale
when you build your application. when you build your application.
@ -342,13 +390,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>environments/*</code> <code>environments/*</code>
</td> </td>
<td> <td>
This folder contains one file for each of your destination environments, This folder contains one file for each of your destination environments,
each exporting simple configuration variables to use in your application. each exporting simple configuration variables to use in your application.
@ -363,13 +414,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>favicon.ico</code> <code>favicon.ico</code>
</td> </td>
<td> <td>
Every site wants to look good on the bookmark bar. Every site wants to look good on the bookmark bar.
Get started with your very own Angular icon. Get started with your very own Angular icon.
@ -379,13 +433,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>index.html</code> <code>index.html</code>
</td> </td>
<td> <td>
The main HTML page that is served when someone visits your site. The main HTML page that is served when someone visits your site.
Most of the time you'll never need to edit it. Most of the time you'll never need to edit it.
@ -397,13 +454,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>main.ts</code> <code>main.ts</code>
</td> </td>
<td> <td>
The main entry point for your app. The main entry point for your app.
Compiles the application with the [JIT compiler](glossary) Compiles the application with the [JIT compiler](glossary)
@ -416,13 +476,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>polyfills.ts</code> <code>polyfills.ts</code>
</td> </td>
<td> <td>
Different browsers have different levels of support of the web standards. Different browsers have different levels of support of the web standards.
Polyfills help normalize those differences. Polyfills help normalize those differences.
@ -434,13 +497,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>styles.css</code> <code>styles.css</code>
</td> </td>
<td> <td>
Your global styles go here. Your global styles go here.
Most of the time you'll want to have local styles in your components for easier maintenance, Most of the time you'll want to have local styles in your components for easier maintenance,
@ -451,13 +517,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>test.ts</code> <code>test.ts</code>
</td> </td>
<td> <td>
This is the main entry point for your unit tests. This is the main entry point for your unit tests.
It has some custom configuration that might be unfamiliar, but it's not something you'll It has some custom configuration that might be unfamiliar, but it's not something you'll
@ -468,13 +537,16 @@ Any files outside of this folder are meant to support building your app.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.{app|spec}.json</code> <code>tsconfig.{app|spec}.json</code>
</td> </td>
<td> <td>
TypeScript compiler configuration for the Angular app (`tsconfig.app.json`) TypeScript compiler configuration for the Angular app (`tsconfig.app.json`)
and for the unit tests (`tsconfig.spec.json`). and for the unit tests (`tsconfig.spec.json`).
@ -494,22 +566,28 @@ The `src/` folder is just one of the items inside the project's root folder.
Other files help you build, test, maintain, document, and deploy the app. Other files help you build, test, maintain, document, and deploy the app.
These files go in the root folder next to `src/`. These files go in the root folder next to `src/`.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
my-app my-app
<aio-folder> <aio-folder>
e2e e2e
<aio-file> <aio-file>
app.e2e-spec.ts app.e2e-spec.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.po.ts app.po.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.e2e.json tsconfig.e2e.json
</aio-file> </aio-file>
@ -518,56 +596,67 @@ These files go in the root folder next to `src/`.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules/... node_modules/...
</aio-file> </aio-file>
<aio-file> <aio-file>
src/... src/...
</aio-file> </aio-file>
<aio-file> <aio-file>
.angular-cli.json .angular-cli.json
</aio-file> </aio-file>
<aio-file> <aio-file>
.editorconfig .editorconfig
</aio-file> </aio-file>
<aio-file> <aio-file>
.gitignore .gitignore
</aio-file> </aio-file>
<aio-file> <aio-file>
karma.conf.js karma.conf.js
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
<aio-file> <aio-file>
protractor.conf.js protractor.conf.js
</aio-file> </aio-file>
<aio-file> <aio-file>
README.md README.md
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
<aio-file> <aio-file>
tslint.json tslint.json
</aio-file> </aio-file>
@ -579,30 +668,37 @@ These files go in the root folder next to `src/`.
</aio-filetree> </aio-filetree>
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
</style> </style>
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
@ -611,13 +707,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>e2e/</code> <code>e2e/</code>
</td> </td>
<td> <td>
Inside `e2e/` live the End-to-End tests. Inside `e2e/` live the End-to-End tests.
They shouldn't be inside `src/` because e2e tests are really a separate app that They shouldn't be inside `src/` because e2e tests are really a separate app that
@ -629,13 +728,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>node_modules/</code> <code>node_modules/</code>
</td> </td>
<td> <td>
`Node.js` creates this folder and puts all third party modules listed in `Node.js` creates this folder and puts all third party modules listed in
`package.json` inside of it. `package.json` inside of it.
@ -645,13 +747,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.angular-cli.json</code> <code>.angular-cli.json</code>
</td> </td>
<td> <td>
Configuration for Angular CLI. Configuration for Angular CLI.
In this file you can set several defaults and also configure what files are included In this file you can set several defaults and also configure what files are included
@ -663,13 +768,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.editorconfig</code> <code>.editorconfig</code>
</td> </td>
<td> <td>
Simple configuration for your editor to make sure everyone that uses your project Simple configuration for your editor to make sure everyone that uses your project
has the same basic configuration. has the same basic configuration.
@ -681,13 +789,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.gitignore</code> <code>.gitignore</code>
</td> </td>
<td> <td>
Git configuration to make sure autogenerated files are not commited to source control. Git configuration to make sure autogenerated files are not commited to source control.
</td> </td>
@ -696,13 +807,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma.conf.js</code> <code>karma.conf.js</code>
</td> </td>
<td> <td>
Unit test configuration for the [Karma test runner](https://karma-runner.github.io), Unit test configuration for the [Karma test runner](https://karma-runner.github.io),
used when running `ng test`. used when running `ng test`.
@ -712,13 +826,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>package.json</code> <code>package.json</code>
</td> </td>
<td> <td>
`npm` configuration listing the third party packages your project uses. `npm` configuration listing the third party packages your project uses.
You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here. You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here.
@ -728,13 +845,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>protractor.conf.js</code> <code>protractor.conf.js</code>
</td> </td>
<td> <td>
End-to-end test configuration for [Protractor](http://www.protractortest.org/), End-to-end test configuration for [Protractor](http://www.protractortest.org/),
used when running `ng e2e`. used when running `ng e2e`.
@ -744,13 +864,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>README.md</code> <code>README.md</code>
</td> </td>
<td> <td>
Basic documentation for your project, pre-filled with CLI command information. Basic documentation for your project, pre-filled with CLI command information.
Make sure to enhance it with project documentation so that anyone Make sure to enhance it with project documentation so that anyone
@ -761,13 +884,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.json</code> <code>tsconfig.json</code>
</td> </td>
<td> <td>
TypeScript compiler configuration for your IDE to pick up and give you helpful tooling. TypeScript compiler configuration for your IDE to pick up and give you helpful tooling.
</td> </td>
@ -776,13 +902,16 @@ These files go in the root folder next to `src/`.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tslint.json</code> <code>tslint.json</code>
</td> </td>
<td> <td>
Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with
[Codelyzer](http://codelyzer.com/), used when running `ng lint`. [Codelyzer](http://codelyzer.com/), used when running `ng lint`.

View File

@ -1,4 +1,4 @@
# _boilerplate files # boilerplate files
**/src/styles.css **/src/styles.css
**/src/systemjs-angular-loader.js **/src/systemjs-angular-loader.js
**/src/systemjs.config.js **/src/systemjs.config.js
@ -23,12 +23,18 @@ protractor-helpers.js
dist/ dist/
# special # special
!_boilerplate/
!/* !/*
!systemjs.config.*.js
!*.1.* !*.1.*
!*.2.* !*.2.*
!*.3.* !*.3.*
*.1.js
*.2.js
*.3.js
*.1.js.map
*.2.js.map
*.3.js.map
!systemjs.config.*.js
!karma-test-shim.*.js
# AngularJS files # AngularJS files
!**/*.ajs.js !**/*.ajs.js

View File

@ -25,6 +25,15 @@ to a<span if-docs="ts"> module</span> factory, meaning you don't need to include
Ahead-of-time compiled applications also benefit from decreased load time and increased performance. Ahead-of-time compiled applications also benefit from decreased load time and increased performance.
~~~
## Annotation
~~~ {.l-sub-section}
In practice, a synonym for [Decoration](glossary#decorator).
~~~ ~~~
@ -58,7 +67,6 @@ binding an HTML object property to a data object property.
Sometimes refers to a [dependency-injection](glossary#dependency-injection) binding Sometimes refers to a [dependency-injection](glossary#dependency-injection) binding
between a "token"&mdash;also referred to as a "key"&mdash;and a dependency [provider](glossary#provider). between a "token"&mdash;also referred to as a "key"&mdash;and a dependency [provider](glossary#provider).
When using this more rare usage, be clear in context.
~~~ ~~~
@ -67,9 +75,12 @@ When using this more rare usage, be clear in context.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`).
Bootstrapping identifies an application's top level "root" [component](glossary#component),
which is the first component that is loaded for the application.
For more information, see the [Setup](!{docsLatest}/guide/setup) page.
You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`). Bootstrapping identifies an application's top level "root" [component](glossary#component), which is the first component that is loaded for the application. You can bootstrap multiple apps in the same `index.html`, each app with its own top-level root.
For more information, see the [Setup](!{docsLatest}/guide/setup) page.You can bootstrap multiple apps in the same `index.html`, each app with its own top-level root.
~~~ ~~~
@ -154,6 +165,48 @@ operations and supporting declaration syntax.
* [Two-way data binding with ngModel](!{docsLatest}/guide/template-syntax). * [Two-way data binding with ngModel](!{docsLatest}/guide/template-syntax).
~~~
{@a decorator}
{@a decoration}
## Decorator | decoration
~~~ {.l-sub-section}
A *function* that adds metadata to a class, its members (properties, methods) and function arguments.
Decorators are a JavaScript language [feature](https://github.com/wycats/javascript-decorators), implemented in TypeScript and proposed for ES2016 (also known as ES7).
To apply a decorator, position it immediately above or to the left of the item it decorates.
Angular has its own set of decorators to help it interoperate with your application parts.
The following example is a `@Component` decorator that identifies a
class as an Angular [component](glossary#component) and an `@Input` decorator applied to the `name` property
of that component. The elided object argument to the `@Component` decorator would contain the pertinent component metadata.
```
@Component({...})
export class AppComponent {
constructor(@Inject('SpecialFoo') public foo:Foo) {}
@Input() name:string;
}
```
The scope of a decorator is limited to the language feature
that it decorates. None of the decorations shown here will "leak" to other
classes that follow it in the file.
~~~ {.alert.is-important}
Always include parentheses `()` when applying a decorator.
~~~
~~~ ~~~
@ -226,7 +279,7 @@ Read more in the [Dependency Injection](!{docsLatest}/guide/dependency-injection
An Angular class responsible for creating, reshaping, and interacting with HTML elements An Angular class responsible for creating, reshaping, and interacting with HTML elements
in the browser DOM. The directive is Angular's most fundamental feature. in the browser DOM. The directive is Angular's most fundamental feature.
A directive is ususally associated with an HTML element or attribute. A directive is usually associated with an HTML element or attribute.
This element or attribute is often referred to as the directive itself. This element or attribute is often referred to as the directive itself.
When Angular finds a directive in an HTML template, When Angular finds a directive in an HTML template,
@ -238,23 +291,21 @@ associate with your custom directives. You add this custom markup to HTML templa
as if you were writing native HTML. In this way, directives become extensions of as if you were writing native HTML. In this way, directives become extensions of
HTML itself. HTML itself.
Directives fall into one of the following categories: Directives fall into one of the following categories:
* [Components](glossary#component) combine application logic with an HTML template to * [Components](glossary#component) combine application logic with an HTML template to
render application [views](glossary#view). Components are usually represented as HTML elements. render application [views](glossary#view). Components are usually represented as HTML elements.
They are the building blocks of an Angular application. They are the building blocks of an Angular application.
1. [Attribute directives](glossary#attribute-directive) can listen to and modify the behavior of * [Attribute directives](glossary#attribute-directive) can listen to and modify the behavior of
other HTML elements, attributes, properties, and components. They are usually represented other HTML elements, attributes, properties, and components. They are usually represented
as HTML attributes, hence the name. as HTML attributes, hence the name.
1. [Structural directives](glossary#structural-directive) are responsible for * [Structural directives](glossary#structural-directive) are responsible for
shaping or reshaping HTML layout, typically by adding, removing, or manipulating shaping or reshaping HTML layout, typically by adding, removing, or manipulating
elements and their children. elements and their children.
~~~ ~~~
@ -349,6 +400,7 @@ renders as text. That text may be concatenated with neighboring text
before it is assigned to an element property before it is assigned to an element property
or displayed between element tags, as in this example. or displayed between element tags, as in this example.
<code-example language="html" escape="html"> <code-example language="html" escape="html">
<label>My current hero is {{hero.name}}</label> <label>My current hero is {{hero.name}}</label>
@ -421,7 +473,6 @@ Read more in the [Lifecycle Hooks](!{docsLatest}/guide/lifecycle-hooks) page.
~~~ {.alert.is-important} ~~~ {.alert.is-important}
Angular has the following types of modules: Angular has the following types of modules:
@ -501,6 +552,7 @@ display in a [view](glossary#view).
Here's an example that uses the built-in `currency` pipe to display Here's an example that uses the built-in `currency` pipe to display
a numeric value in the local currency. a numeric value in the local currency.
<code-example language="html" escape="html"> <code-example language="html" escape="html">
<label>Price: </label>{{product.price | currency}} <label>Price: </label>{{product.price | currency}}
@ -527,6 +579,24 @@ It relates a lookup token to code&mdash;sometimes called a "recipe"&mdash;that c
{@a Q} {@a Q}
## Reactive forms
~~~ {.l-sub-section}
A technique for building Angular forms through code in a component.
The alternative technique is [template-driven forms](glossary#template-driven-forms).
When building reactive forms:
- The "source of truth" is the component. The validation is defined using code in the component.
- Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
- The template input elements do *not* use `ngModel`.
- The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
Reactive forms are powerful, flexible, and a good choice for more complex data-entry form scenarios, such as dynamic generation of form controls.
~~~
## Router ## Router
~~~ {.l-sub-section} ~~~ {.l-sub-section}
@ -539,6 +609,17 @@ replace one view with another.
The Angular component router is a richly featured mechanism for configuring and managing the entire view navigation process, including the creation and destruction The Angular component router is a richly featured mechanism for configuring and managing the entire view navigation process, including the creation and destruction
of views. of views.
~~~
## Router module
~~~ {.l-sub-section}
A separate [Angular module](glossary#angular-module) that provides the necessary service providers and directives for navigating through application views.
For more information, see the [Routing & Navigation](!{docsLatest}/guide/router) page.
~~~ ~~~
## Routing component ## Routing component
@ -553,6 +634,30 @@ For more information, see the [Routing & Navigation](!{docsLatest}/guide/router)
~~~ ~~~
## Scoped package
~~~ {.l-sub-section}
A way to group related *npm* packages.
Read more at the [npm-scope](https://docs.npmjs.com/misc/scope) page.
Angular modules are delivered within *scoped packages* such as `@angular/core`,
`@angular/common`, `@angular/platform-browser-dynamic`, `@angular/http`, and `@angular/router`.
Import a scoped package the same way that you import a normal package.
The only difference, from a consumer perspective,
is that the scoped package name begins with the Angular *scope name*, `@angular`.
<code-example path="architecture/src/app/app.component.ts" linenums="false" title="architecture/ts/src/app/app.component.ts (import)" region="import">
</code-example>
~~~
## Service ## Service
~~~ {.l-sub-section} ~~~ {.l-sub-section}
@ -579,10 +684,8 @@ For more information, see the [Services](!{docsLatest}/tutorial/toh-pt4) page of
{@a snake-case} {@a snake-case}
## snake_case ## snake_case
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The practice of writing compound words or phrases such that an The practice of writing compound words or phrases such that an
underscore (`_`) separates one word from the next. This form is also known as *underscore case*. underscore (`_`) separates one word from the next. This form is also known as *underscore case*.
@ -603,7 +706,7 @@ A category of [directive](glossary#directive) that can
shape or reshape HTML layout, typically by adding and removing elements in the DOM. shape or reshape HTML layout, typically by adding and removing elements in the DOM.
The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are well-known examples. The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are well-known examples.
Read more in the [_Structural Directives_](!{docsLatest}/guide/structural-directives) guide. Read more in the [Structural Directives](!{docsLatest}/guide/structural-directives) page.
~~~ ~~~
@ -618,6 +721,26 @@ the support and guidance of an Angular [directive](glossary#directive),
most notably a [component](glossary#component). most notably a [component](glossary#component).
~~~
## Template-driven forms
~~~ {.l-sub-section}
A technique for building Angular forms using HTML forms and input elements in the view.
The alternate technique is [Reactive Forms](glossary#reactive-forms).
When building template-driven forms:
- The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
- [Two-way binding](glossary#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
- Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
- The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data-entry form scenarios.
Read about how to build template-driven forms
in the [Forms](!{docsLatest}/guide/forms) page.
~~~ ~~~
@ -702,7 +825,6 @@ under the control of a [router](glossary#router).
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A mechanism for encapsulating and intercepting A mechanism for encapsulating and intercepting
a JavaScript application's asynchronous activity. a JavaScript application's asynchronous activity.

View File

@ -31,25 +31,31 @@ by mapping AngularJS syntax to the equivalent Angular syntax.
Templates are the user-facing part of an Angular application and are written in HTML. Templates are the user-facing part of an Angular application and are written in HTML.
The following table lists some of the key AngularJS template features with their equivalent Angular template syntax. The following table lists some of the key AngularJS template features with their equivalent Angular template syntax.
<table width="100%"> <table width="100%">
<col width="50%"> <col width="50%">
</col> </col>
<col width="50%"> <col width="50%">
</col> </col>
<tr> <tr>
<th> <th>
AngularJS AngularJS
</th> </th>
<th> <th>
Angular Angular
</th> </th>
@ -58,10 +64,13 @@ The following table lists some of the key AngularJS template features with their
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Bindings/interpolation ### Bindings/interpolation
<code-example> <code-example>
Your favorite hero is: {{vm.favoriteHero}} Your favorite hero is: {{vm.favoriteHero}}
</code-example> </code-example>
@ -76,9 +85,11 @@ The following table lists some of the key AngularJS template features with their
</td> </td>
<td> <td>
### Bindings/interpolation ### Bindings/interpolation
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false">
</code-example> </code-example>
@ -96,10 +107,13 @@ The following table lists some of the key AngularJS template features with their
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Filters ### Filters
<code-example> <code-example>
&lt;td>{{movie.title | uppercase}}&lt;/td> &lt;td>{{movie.title | uppercase}}&lt;/td>
</code-example> </code-example>
@ -110,9 +124,11 @@ The following table lists some of the key AngularJS template features with their
</td> </td>
<td> <td>
### Pipes ### Pipes
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false">
</code-example> </code-example>
@ -128,10 +144,13 @@ The following table lists some of the key AngularJS template features with their
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Local variables ### Local variables
<code-example format=""> <code-example format="">
&lt;tr ng-repeat="movie in vm.movies"> &lt;tr ng-repeat="movie in vm.movies">
&lt;td>{{movie.title}}&lt;/td> &lt;td>{{movie.title}}&lt;/td>
@ -142,9 +161,11 @@ The following table lists some of the key AngularJS template features with their
</td> </td>
<td> <td>
### Input variables ### Input variables
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="local" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="local" linenums="false">
</code-example> </code-example>
@ -168,25 +189,31 @@ AngularJS provides more than seventy built-in directives for templates.
Many of them aren't needed in Angular because of its more capable and expressive binding system. Many of them aren't needed in Angular because of its more capable and expressive binding system.
The following are some of the key AngularJS built-in directives and their equivalents in Angular. The following are some of the key AngularJS built-in directives and their equivalents in Angular.
<table width="100%"> <table width="100%">
<col width="50%"> <col width="50%">
</col> </col>
<col width="50%"> <col width="50%">
</col> </col>
<tr> <tr>
<th> <th>
AngularJS AngularJS
</th> </th>
<th> <th>
Angular Angular
</th> </th>
@ -195,10 +222,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-app ### ng-app
<code-example> <code-example>
&lt;body ng-app="movieHunter"> &lt;body ng-app="movieHunter">
</code-example> </code-example>
@ -211,15 +241,18 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bootstrapping ### Bootstrapping
<code-example path="cb-ajs-quick-reference/src/main.ts" linenums="false"> <code-example path="cb-ajs-quick-reference/src/main.ts" linenums="false">
</code-example> </code-example>
<br> <br>
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example> </code-example>
@ -236,10 +269,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-class ### ng-class
<code-example format=""> <code-example format="">
&lt;div ng-class="{active: isActive}"> &lt;div ng-class="{active: isActive}">
&lt;div ng-class="{active: isActive, &lt;div ng-class="{active: isActive,
@ -257,9 +293,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### ngClass ### ngClass
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false">
</code-example> </code-example>
@ -283,10 +321,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-click ### ng-click
<code-example format=""> <code-example format="">
&lt;button ng-click="vm.toggleImage()"> &lt;button ng-click="vm.toggleImage()">
&lt;button ng-click="vm.toggleImage($event)"> &lt;button ng-click="vm.toggleImage($event)">
@ -301,9 +342,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bind to the `click` event ### Bind to the `click` event
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false">
</code-example> </code-example>
@ -332,10 +375,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-controller ### ng-controller
<code-example format=""> <code-example format="">
&lt;div ng-controller="MovieListCtrl as vm"> &lt;div ng-controller="MovieListCtrl as vm">
</code-example> </code-example>
@ -346,9 +392,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Component decorator ### Component decorator
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example> </code-example>
@ -364,8 +412,10 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-hide ### ng-hide
In AngularJS, the `ng-hide` directive shows or hides the associated HTML element based on In AngularJS, the `ng-hide` directive shows or hides the associated HTML element based on
@ -373,6 +423,7 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bind to the `hidden` property ### Bind to the `hidden` property
In Angular, you use property binding; there is no built-in *hide* directive. In Angular, you use property binding; there is no built-in *hide* directive.
@ -383,10 +434,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-href ### ng-href
<code-example format=""> <code-example format="">
&lt;a ng-href="angularDocsUrl">Angular Docs&lt;/a> &lt;a ng-href="angularDocsUrl">Angular Docs&lt;/a>
</code-example> </code-example>
@ -396,6 +450,7 @@ The following are some of the key AngularJS built-in directives and their equiva
fetches from that URL. fetches from that URL.
In AngularJS, the `ng-href` is often used to activate a route as part of navigation. In AngularJS, the `ng-href` is often used to activate a route as part of navigation.
<code-example format=""> <code-example format="">
&lt;a ng-href="#movies">Movies&lt;/a> &lt;a ng-href="#movies">Movies&lt;/a>
</code-example> </code-example>
@ -404,9 +459,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bind to the `href` property ### Bind to the `href` property
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="href" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="href" linenums="false">
</code-example> </code-example>
@ -419,6 +476,7 @@ The following are some of the key AngularJS built-in directives and their equiva
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example. In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false">
</code-example> </code-example>
@ -432,10 +490,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-if ### ng-if
<code-example format=""> <code-example format="">
&lt;table ng-if="movies.length"> &lt;table ng-if="movies.length">
</code-example> </code-example>
@ -447,9 +508,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### *ngIf ### *ngIf
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false">
</code-example> </code-example>
@ -467,10 +530,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-model ### ng-model
<code-example format=""> <code-example format="">
&lt;input ng-model="vm.favoriteHero"/> &lt;input ng-model="vm.favoriteHero"/>
</code-example> </code-example>
@ -480,9 +546,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### ngModel ### ngModel
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false">
</code-example> </code-example>
@ -499,10 +567,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-repeat ### ng-repeat
<code-example format=""> <code-example format="">
&lt;tr ng-repeat="movie in vm.movies"> &lt;tr ng-repeat="movie in vm.movies">
</code-example> </code-example>
@ -514,9 +585,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### *ngFor ### *ngFor
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false">
</code-example> </code-example>
@ -538,10 +611,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-show ### ng-show
<code-example format=""> <code-example format="">
&lt;h3 ng-show="vm.favoriteHero"> &lt;h3 ng-show="vm.favoriteHero">
Your favorite hero is: {{vm.favoriteHero}} Your favorite hero is: {{vm.favoriteHero}}
@ -555,9 +631,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bind to the `hidden` property ### Bind to the `hidden` property
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false">
</code-example> </code-example>
@ -578,10 +656,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-src ### ng-src
<code-example format=""> <code-example format="">
&lt;img ng-src="{{movie.imageurl}}"> &lt;img ng-src="{{movie.imageurl}}">
</code-example> </code-example>
@ -592,9 +673,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### Bind to the `src` property ### Bind to the `src` property
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="src" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="src" linenums="false">
</code-example> </code-example>
@ -610,10 +693,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-style ### ng-style
<code-example format=""> <code-example format="">
&lt;div ng-style="{color: colorPreference}"> &lt;div ng-style="{color: colorPreference}">
</code-example> </code-example>
@ -627,9 +713,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### ngStyle ### ngStyle
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false">
</code-example> </code-example>
@ -651,10 +739,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### ng-switch ### ng-switch
<code-example format=""> <code-example format="">
&lt;div ng-switch="vm.favoriteHero && &lt;div ng-switch="vm.favoriteHero &&
vm.checkMovieHero(vm.favoriteHero)"> vm.checkMovieHero(vm.favoriteHero)">
@ -680,9 +771,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td> </td>
<td> <td>
### ngSwitch ### ngSwitch
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false">
</code-example> </code-example>
@ -718,25 +811,31 @@ Angular **pipes** provide formatting and transformation for data in the template
Many of the built-in filters in AngularJS have corresponding pipes in Angular. Many of the built-in filters in AngularJS have corresponding pipes in Angular.
For more information on pipes, see [Pipes](guide/pipes). For more information on pipes, see [Pipes](guide/pipes).
<table width="100%"> <table width="100%">
<col width="50%"> <col width="50%">
</col> </col>
<col width="50%"> <col width="50%">
</col> </col>
<tr> <tr>
<th> <th>
AngularJS AngularJS
</th> </th>
<th> <th>
Angular Angular
</th> </th>
@ -745,10 +844,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### currency ### currency
<code-example> <code-example>
&lt;td>{{movie.price | currency}}&lt;/td> &lt;td>{{movie.price | currency}}&lt;/td>
</code-example> </code-example>
@ -757,9 +859,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### currency ### currency
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false">
</code-example> </code-example>
@ -771,10 +875,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### date ### date
<code-example> <code-example>
&lt;td>{{movie.releaseDate | date}}&lt;/td> &lt;td>{{movie.releaseDate | date}}&lt;/td>
</code-example> </code-example>
@ -783,9 +890,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### date ### date
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="date" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="date" linenums="false">
</code-example> </code-example>
@ -798,10 +907,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### filter ### filter
<code-example> <code-example>
&lt;tr ng-repeat="movie in movieList | filter: {title:listFilter}"> &lt;tr ng-repeat="movie in movieList | filter: {title:listFilter}">
</code-example> </code-example>
@ -810,6 +922,7 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### none ### none
For performance reasons, no comparable pipe exists in Angular. Do all your filtering in the component. If you need the same filtering code in several templates, consider building a custom pipe. For performance reasons, no comparable pipe exists in Angular. Do all your filtering in the component. If you need the same filtering code in several templates, consider building a custom pipe.
@ -820,10 +933,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### json ### json
<code-example> <code-example>
&lt;pre>{{movie | json}}&lt;/pre> &lt;pre>{{movie | json}}&lt;/pre>
</code-example> </code-example>
@ -832,9 +948,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### json ### json
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="json" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="json" linenums="false">
</code-example> </code-example>
@ -846,10 +964,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### limitTo ### limitTo
<code-example> <code-example>
&lt;tr ng-repeat="movie in movieList | limitTo:2:0"> &lt;tr ng-repeat="movie in movieList | limitTo:2:0">
</code-example> </code-example>
@ -859,9 +980,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### slice ### slice
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false">
</code-example> </code-example>
@ -876,10 +999,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### lowercase ### lowercase
<code-example> <code-example>
&lt;div>{{movie.title | lowercase}}&lt;/div> &lt;div>{{movie.title | lowercase}}&lt;/div>
</code-example> </code-example>
@ -888,9 +1014,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### lowercase ### lowercase
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false">
</code-example> </code-example>
@ -902,10 +1030,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### number ### number
<code-example> <code-example>
&lt;td>{{movie.starRating | number}}&lt;/td> &lt;td>{{movie.starRating | number}}&lt;/td>
</code-example> </code-example>
@ -914,9 +1045,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### number ### number
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="number" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="number" linenums="false">
</code-example> </code-example>
@ -933,10 +1066,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### orderBy ### orderBy
<code-example> <code-example>
&lt;tr ng-repeat="movie in movieList | orderBy : 'title'"> &lt;tr ng-repeat="movie in movieList | orderBy : 'title'">
</code-example> </code-example>
@ -946,6 +1082,7 @@ For more information on pipes, see [Pipes](guide/pipes).
</td> </td>
<td> <td>
### none ### none
For performance reasons, no comparable pipe exists in Angular. For performance reasons, no comparable pipe exists in Angular.
@ -973,25 +1110,31 @@ In Angular, you build a **component**.
Because much AngularJS code is in JavaScript, JavaScript code is shown in the AngularJS column. Because much AngularJS code is in JavaScript, JavaScript code is shown in the AngularJS column.
The Angular code is shown using TypeScript. The Angular code is shown using TypeScript.
<table width="100%"> <table width="100%">
<col width="50%"> <col width="50%">
</col> </col>
<col width="50%"> <col width="50%">
</col> </col>
<tr> <tr>
<th> <th>
AngularJS AngularJS
</th> </th>
<th> <th>
Angular Angular
</th> </th>
@ -1000,10 +1143,13 @@ The Angular code is shown using TypeScript.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### IIFE ### IIFE
<code-example> <code-example>
(function () { (function () {
... ...
@ -1016,6 +1162,7 @@ The Angular code is shown using TypeScript.
</td> </td>
<td> <td>
### none ### none
This is a nonissue in Angular because ES 2015 modules This is a nonissue in Angular because ES 2015 modules
@ -1029,10 +1176,13 @@ The Angular code is shown using TypeScript.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Angular modules ### Angular modules
<code-example> <code-example>
angular.module("movieHunter", ["ngRoute"]); angular.module("movieHunter", ["ngRoute"]);
</code-example> </code-example>
@ -1042,9 +1192,11 @@ The Angular code is shown using TypeScript.
</td> </td>
<td> <td>
### Angular modules ### Angular modules
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example> </code-example>
@ -1060,10 +1212,13 @@ The Angular code is shown using TypeScript.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Controller registration ### Controller registration
<code-example> <code-example>
angular angular
.module("movieHunter") .module("movieHunter")
@ -1080,9 +1235,11 @@ The Angular code is shown using TypeScript.
</td> </td>
<td> <td>
### Component decorator ### Component decorator
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example> </code-example>
@ -1101,10 +1258,13 @@ The Angular code is shown using TypeScript.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Controller function ### Controller function
<code-example> <code-example>
function MovieListCtrl(movieService) { function MovieListCtrl(movieService) {
} }
@ -1114,9 +1274,11 @@ The Angular code is shown using TypeScript.
</td> </td>
<td> <td>
### Component class ### Component class
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -1133,10 +1295,13 @@ The Angular code is shown using TypeScript.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Dependency injection ### Dependency injection
<code-example> <code-example>
MovieListCtrl.$inject = ['MovieService']; MovieListCtrl.$inject = ['MovieService'];
function MovieListCtrl(movieService) { function MovieListCtrl(movieService) {
@ -1151,9 +1316,11 @@ The Angular code is shown using TypeScript.
</td> </td>
<td> <td>
### Dependency injection ### Dependency injection
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false">
</code-example> </code-example>
@ -1184,25 +1351,31 @@ As the application grows over time, the styles for the many parts of the applica
merge, which can cause unexpected results. merge, which can cause unexpected results.
In Angular, you can still define style sheets for your entire application. But now you can In Angular, you can still define style sheets for your entire application. But now you can
also encapsulate a style sheet within a specific component. also encapsulate a style sheet within a specific component.
<table width="100%"> <table width="100%">
<col width="50%"> <col width="50%">
</col> </col>
<col width="50%"> <col width="50%">
</col> </col>
<tr> <tr>
<th> <th>
AngularJS AngularJS
</th> </th>
<th> <th>
Angular Angular
</th> </th>
@ -1211,10 +1384,13 @@ also encapsulate a style sheet within a specific component.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
### Link tag ### Link tag
<code-example> <code-example>
&lt;link href="styles.css" rel="stylesheet" /> &lt;link href="styles.css" rel="stylesheet" />
</code-example> </code-example>
@ -1224,9 +1400,11 @@ also encapsulate a style sheet within a specific component.
</td> </td>
<td> <td>
### Link tag ### Link tag
<code-example path="cb-ajs-quick-reference/src/index.html" region="style" linenums="false"> <code-example path="cb-ajs-quick-reference/src/index.html" region="style" linenums="false">
</code-example> </code-example>
@ -1236,6 +1414,7 @@ also encapsulate a style sheet within a specific component.
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
a style sheet for a particular component. a style sheet for a particular component.
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false"> <code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false">
</code-example> </code-example>

View File

@ -54,6 +54,7 @@ The examples in this page are available as a <live-example></live-example>.
{@a example-transitioning-between-states} {@a example-transitioning-between-states}
## Quickstart example: Transitioning between two states ## Quickstart example: Transitioning between two states
<figure> <figure>
<img src="assets/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> </figure>
@ -65,12 +66,14 @@ Animations are defined inside `@Component` metadata. Before you can add animatio
to import a few animation-specific imports and functions: to import a few animation-specific imports and functions:
<code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false"> <code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false">
</code-example> </code-example>
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -80,6 +83,7 @@ metadata. It uses animations to transition between two states: `active` and `ina
hero is active, the element appears in a slightly larger size and lighter color. hero is active, the element appears in a slightly larger size and lighter color.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -98,6 +102,7 @@ Now, using the `[@triggerName]` syntax, attach the animation that you just defin
one or more elements in the component's template. one or more elements in the component's template.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -110,6 +115,7 @@ With this setup, an animated transition appears whenever a hero object changes s
Here's the full component implementation: Here's the full component implementation:
<code-example path="animations/src/app/hero-list-basic.component.ts"> <code-example path="animations/src/app/hero-list-basic.component.ts">
</code-example> </code-example>
@ -128,6 +134,7 @@ component's template.
You can define *styles* for each animation state: You can define *styles* for each animation state:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false">
</code-example> </code-example>
@ -140,11 +147,13 @@ After you define states, you can define *transitions* between the states. Each t
controls the timing of switching between one set of styles and the next: controls the timing of switching between one set of styles and the next:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<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> <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> </figure>
@ -153,6 +162,7 @@ If several transitions have the same timing configuration, you can combine
them into the same `transition` definition: them into the same `transition` definition:
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -161,6 +171,7 @@ When both directions of a transition have the same timing, as in the previous
example, you can use the shorthand syntax `<=>`: example, you can use the shorthand syntax `<=>`:
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -172,6 +183,7 @@ When the transition finishes, none of these styles are kept because they're not
defined in a `state`. defined in a `state`.
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -184,6 +196,7 @@ transitions that apply regardless of which state the animation is in. For exampl
* The `active => *` transition applies when the element's state changes from `active` to anything else. * The `active => *` transition applies when the element's state changes from `active` to anything else.
* The `* => *` transition applies when *any* change between two states takes place. * The `* => *` transition applies when *any* change between two states takes place.
<figure class='image-display'> <figure class='image-display'>
<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> <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> </figure>
@ -198,6 +211,7 @@ leave animations.
For example the `* => void` transition applies when the element leaves the view, For example the `* => void` transition applies when the element leaves the view,
regardless of what state it was in before it left. regardless of what state it was in before it left.
<figure class='image-display'> <figure class='image-display'>
<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> <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> </figure>
@ -205,6 +219,7 @@ regardless of what state it was in before it left.
The wildcard state `*` also matches `void`. The wildcard state `*` also matches `void`.
## Example: Entering and leaving ## Example: Entering and leaving
<figure> <figure>
<img src="assets/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> </figure>
@ -218,6 +233,7 @@ entering and leaving of elements:
For example, in the `animations` !{_array} below there are two transitions that use 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. the `void => *` and `* => void` syntax to animate the element in and out of the view.
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -231,6 +247,7 @@ and leaves to the right.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
These two common animations have their own aliases: These two common animations have their own aliases:
<code-example language="typescript"> <code-example language="typescript">
transition(':enter', [ ... ]); // void => * transition(':enter', [ ... ]); // void => *
transition(':leave', [ ... ]); // * => void transition(':leave', [ ... ]); // * => void
@ -242,6 +259,7 @@ These two common animations have their own aliases:
~~~ ~~~
## Example: Entering and leaving from different states ## Example: Entering and leaving from different states
<figure> <figure>
<img src="assets/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"> </img> <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> </figure>
@ -258,12 +276,14 @@ is:
This gives you fine-grained control over each transition: This gives you fine-grained control over each transition:
<figure class='image-display'> <figure class='image-display'>
<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> <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> </figure>
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -288,6 +308,7 @@ If you don't provide a unit when specifying dimension, Angular assumes the defau
* `50` is the same as saying `'50px'` * `50` is the same as saying `'50px'`
## Automatic property calculation ## Automatic property calculation
<figure> <figure>
<img src="assets/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> </figure>
@ -304,6 +325,7 @@ In this example, the leave animation takes whatever height the element has befor
leaves and animates from that height to zero: leaves and animates from that height to zero:
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -342,6 +364,7 @@ and the delay (or as the *second* value when there is no delay):
* Wait for 100ms and then run for 200ms, with easing: `'0.2s 100ms ease-out'` * Wait for 100ms and then run for 200ms, with easing: `'0.2s 100ms ease-out'`
* Run for 200ms, with easing: `'0.2s ease-in-out'` * Run for 200ms, with easing: `'0.2s ease-in-out'`
<figure> <figure>
<img src="assets/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> </figure>
@ -354,11 +377,13 @@ slight delay of 10 milliseconds as specified in `'0.2s 10 ease-out'`:
<code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
## Multi-step animations with keyframes ## Multi-step animations with keyframes
<figure> <figure>
<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> <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> </figure>
@ -374,6 +399,7 @@ This example adds some "bounce" to the enter and leave animations with
keyframes: keyframes:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -386,6 +412,7 @@ Defining offsets for keyframes is optional. If you omit them, offsets with even
spacing are automatically assigned. For example, three keyframes without predefined spacing are automatically assigned. For example, three keyframes without predefined
offsets receive offsets `0`, `0.5`, and `1`. offsets receive offsets `0`, `0.5`, and `1`.
## Parallel animation groups ## Parallel animation groups
<figure> <figure>
<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> <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> </figure>
@ -402,6 +429,7 @@ enter and leave allows for two different timing configurations. Both
are applied to the same element in parallel, but run independently of each other: are applied to the same element in parallel, but run independently of each other:
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -415,6 +443,7 @@ In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
those callbacks like this: those callbacks like this:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false"> <code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false">
</code-example> </code-example>

View File

@ -114,13 +114,16 @@ Take the <a href='../guide/setup.html'>Setup</a> as a starting point.
A few minor changes to the lone `app.component` lead to these two class and HTML files: A few minor changes to the lone `app.component` lead to these two class and HTML files:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane> </code-pane>
@ -129,6 +132,7 @@ A few minor changes to the lone `app.component` lead to these two class and HTML
</code-tabs> </code-tabs>
Install a few new npm dependencies with the following command: Install a few new npm dependencies with the following command:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm install @angular/compiler-cli @angular/platform-server --save npm install @angular/compiler-cli @angular/platform-server --save
</code-example> </code-example>
@ -143,6 +147,7 @@ Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` on th
then modify it as follows. then modify it as follows.
<code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false"> <code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false">
</code-example> </code-example>
@ -167,6 +172,7 @@ While JIT app URLs are more flexible, stick with _component-relative_ URLs for c
***Compiling the application*** ***Compiling the application***
Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing: Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
node_modules/.bin/ngc -p tsconfig-aot.json node_modules/.bin/ngc -p tsconfig-aot.json
</code-example> </code-example>
@ -176,6 +182,7 @@ Initiate AOT compilation from the command line using the previously installed `n
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Windows users should surround the `ngc` command in double quotes: Windows users should surround the `ngc` command in double quotes:
<code-example format='.'> <code-example format='.'>
"node_modules/.bin/ngc" -p tsconfig-aot.json "node_modules/.bin/ngc" -p tsconfig-aot.json
</code-example> </code-example>
@ -234,13 +241,16 @@ Switch from the `platformBrowserDynamic.bootstrap` used in JIT compilation to
Here is AOT bootstrap in `main.ts` next to the original JIT version: Here is AOT bootstrap in `main.ts` next to the original JIT version:
<code-tabs> <code-tabs>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts"> <code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts"> <code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts">
</code-pane> </code-pane>
@ -290,6 +300,7 @@ What matters is that the code uses ES `import` and `export` statements rather th
~~~ ~~~
In the terminal window, install the Rollup dependencies with this command: In the terminal window, install the Rollup dependencies with this command:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
</code-example> </code-example>
@ -299,6 +310,7 @@ in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this. The cookbook configuration file looks like this.
<code-example path="cb-aot-compiler/rollup-config.js" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" linenums="false">
</code-example> </code-example>
@ -330,6 +342,7 @@ in the final bundle. Using it is straigthforward. Add the following to
the `plugins` !{_array} in `rollup-config.js`: the `plugins` !{_array} in `rollup-config.js`:
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false">
</code-example> </code-example>
@ -341,6 +354,7 @@ This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code
Add the following to the `plugins` !{_array}: Add the following to the `plugins` !{_array}:
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false">
</code-example> </code-example>
@ -360,6 +374,7 @@ the code into an even smaller package going over the wire.
{@a run-rollup} {@a run-rollup}
### Run Rollup ### Run Rollup
Execute the Rollup process with this command: Execute the Rollup process with this command:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
node_modules/.bin/rollup -c rollup-config.js node_modules/.bin/rollup -c rollup-config.js
</code-example> </code-example>
@ -369,6 +384,7 @@ Execute the Rollup process with this command:
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Windows users should surround the `rollup` command in double quotes: Windows users should surround the `rollup` command in double quotes:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
"node_modules/.bin/rollup" -c rollup-config.js "node_modules/.bin/rollup" -c rollup-config.js
</code-example> </code-example>
@ -388,6 +404,7 @@ Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag: Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag:
<code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false"> <code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false">
</code-example> </code-example>
@ -400,6 +417,7 @@ Instead, load the bundle file using a single `<script>` tag **_after_** the `</b
You'll need a web server to host the application. You'll need a web server to host the application.
Use the same `lite-server` employed elsewhere in the documentation: Use the same `lite-server` employed elsewhere in the documentation:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm run lite npm run lite
</code-example> </code-example>
@ -413,33 +431,40 @@ The server starts, launches a browser, and the app should appear.
Here's the pertinent source code: Here's the pertinent source code:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts"> <code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/index.html" path="cb-aot-compiler/src/index.html"> <code-pane title="src/index.html" path="cb-aot-compiler/src/index.html">
</code-pane> </code-pane>
<code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json">
</code-pane> </code-pane>
<code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js"> <code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js">
</code-pane> </code-pane>
@ -457,6 +482,7 @@ You'll rebuild the AOT version of the application every time you make a change.
Those _npm_ commands are long and difficult to remember. Those _npm_ commands are long and difficult to remember.
Add the following _npm_ convenience script to the `package.json` so you can compile and rollup in one command.Open a terminal window and try it. Add the following _npm_ convenience script to the `package.json` so you can compile and rollup in one command.Open a terminal window and try it.
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm run build:aot npm run build:aot
@ -475,6 +501,7 @@ The same source code can be built both ways. Here's one way to do that.
* Delete the script at the bottom of `index-jit.html` that loads `bundle.js` * Delete the script at the bottom of `index-jit.html` that loads `bundle.js`
* Restore the SystemJS scripts like this: * Restore the SystemJS scripts like this:
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false"> <code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false">
</code-example> </code-example>
@ -482,6 +509,7 @@ The same source code can be built both ways. Here's one way to do that.
Notice the slight change to the `system.import` which now specifies `src/app/main-jit`. Notice the slight change to the `system.import` which now specifies `src/app/main-jit`.
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap). That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap).
Open a _different_ terminal window and enter `npm start`. Open a _different_ terminal window and enter `npm start`.
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm start npm start
</code-example> </code-example>
@ -526,13 +554,16 @@ The JIT and AOT apps require their own `index.html` files because they setup and
Here they are for comparison: Here they are for comparison:
<code-tabs> <code-tabs>
<code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html"> <code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html">
</code-pane> </code-pane>
<code-pane title="src/index.html (JIT)" path="toh-6/src/index.html"> <code-pane title="src/index.html (JIT)" path="toh-6/src/index.html">
</code-pane> </code-pane>
@ -553,13 +584,16 @@ The key differences, covered in the [Bootstrap](guide/aot-compiler#bootstrap) se
are evident in these `main` files which can and should reside in the same folder: are evident in these `main` files which can and should reside in the same folder:
<code-tabs> <code-tabs>
<code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts"> <code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts">
</code-pane> </code-pane>
<code-pane title="main.ts (JIT)" path="toh-6/src/main.ts"> <code-pane title="main.ts (JIT)" path="toh-6/src/main.ts">
</code-pane> </code-pane>
@ -576,13 +610,16 @@ AOT requires its own TypeScript configuration settings as well.
You'll need separate TypeScript configuration files such as these: You'll need separate TypeScript configuration files such as these:
<code-tabs> <code-tabs>
<code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json">
</code-pane> </code-pane>
<code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json"> <code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json">
</code-pane> </code-pane>
@ -595,6 +632,7 @@ You'll need separate TypeScript configuration files such as these:
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
@Types and node modules @Types and node modules
</header> </header>
@ -617,6 +655,7 @@ Edit your `tsconfig-aot.json` to fit your project's file structure.
Rollup does the tree shaking as before. Rollup does the tree shaking as before.
<code-example path="toh-6/rollup-config.js" linenums="false"> <code-example path="toh-6/rollup-config.js" linenums="false">
</code-example> </code-example>
@ -643,23 +682,28 @@ Run the JIT-compiled app with `npm start` as for all other JIT examples.
Compiling with AOT presupposes certain supporting files, most of them discussed above. Compiling with AOT presupposes certain supporting files, most of them discussed above.
<code-tabs> <code-tabs>
<code-pane title="src/index.html" path="toh-6/src/index.html"> <code-pane title="src/index.html" path="toh-6/src/index.html">
</code-pane> </code-pane>
<code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js"> <code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js">
</code-pane> </code-pane>
<code-pane title="rollup-config.js" path="toh-6/rollup-config.js"> <code-pane title="rollup-config.js" path="toh-6/rollup-config.js">
</code-pane> </code-pane>
<code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json">
</code-pane> </code-pane>
@ -668,6 +712,7 @@ Compiling with AOT presupposes certain supporting files, most of them discussed
</code-tabs> </code-tabs>
Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script: Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
node copy-dist-files node copy-dist-files
</code-example> </code-example>
@ -681,6 +726,7 @@ You won't do that again until there are updates to `zone.js` or the `core-js` sh
~~~ ~~~
Now AOT-compile the app and launch it with the `lite-server`: Now AOT-compile the app and launch it with the `lite-server`:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm run build:aot && npm run serve:aot npm run build:aot && npm run serve:aot
@ -697,12 +743,14 @@ But the <a href="https://github.com/danvk/source-map-explorer/blob/master/README
tool can be quite revealing. tool can be quite revealing.
Install it: Install it:
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
npm install source-map-explorer --save-dev npm install source-map-explorer --save-dev
</code-example> </code-example>
Run the following command to generate the map. Run the following command to generate the map.
<code-example language="none" class="code-shell"> <code-example language="none" class="code-shell">
node_modules/.bin/source-map-explorer aot/dist/build.js node_modules/.bin/source-map-explorer aot/dist/build.js
@ -713,6 +761,7 @@ showing exactly which application and Angular modules and classes are included i
Here's the map for _Tour of Heroes_. Here's the map for _Tour of Heroes_.
<a href="assets/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'> <figure class='image-display'>
<img src="assets/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> </figure>

View File

@ -14,6 +14,7 @@ The [setup](guide/setup) instructions produce a new project with the following m
You'll evolve this module as your application grows. You'll evolve this module as your application grows.
<code-example path="setup/src/app/app.module.ts" linenums="false"> <code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example> </code-example>
@ -119,6 +120,7 @@ Which brings us to the _bootstrapping_ process itself.
{@a main} {@a main}
<l-main-section> <l-main-section>
</l-main-section> </l-main-section>
@ -134,6 +136,7 @@ and you'll run it in a browser. You can learn about other options later.
The recommended place to bootstrap a JIT-compiled browser application is in a separate file The recommended place to bootstrap a JIT-compiled browser application is in a separate file
in the `src` folder named `src/main.ts` in the `src` folder named `src/main.ts`
<code-example path="setup/src/main.ts" linenums="false"> <code-example path="setup/src/main.ts" linenums="false">
</code-example> </code-example>
@ -148,6 +151,7 @@ creates an instance of the component and inserts it within the element tag ident
The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app` The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app`
so Angular looks for a `<my-app>` tag in the `index.html` like this one ... so Angular looks for a `<my-app>` tag in the `index.html` like this one ...
<code-example path="setup/src/index.html" region="my-app" linenums="false"> <code-example path="setup/src/index.html" region="my-app" linenums="false">
</code-example> </code-example>
@ -156,6 +160,7 @@ so Angular looks for a `<my-app>` tag in the `index.html` like this one ...
This file is very stable. Once you've set it up, you may never change it again. This file is very stable. Once you've set it up, you may never change it again.
<l-main-section> <l-main-section>
</l-main-section> </l-main-section>

View File

@ -22,6 +22,7 @@ responding to user interactions according to the instructions you've provided.
Of course, there is more to it than this. 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. You'll learn the details in the pages that follow. For now, focus on the big picture.
<figure> <figure>
<img src="assets/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> </figure>
@ -43,6 +44,7 @@ Learn these building blocks, and you're on your way.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
<p> <p>
The code referenced on this page is available as a <live-example></live-example>. The code referenced on this page is available as a <live-example></live-example>.
</p> </p>
@ -53,6 +55,7 @@ Learn these building blocks, and you're on your way.
## Modules ## Modules
<figure> <figure>
<img src="assets/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> </figure>
@ -98,6 +101,7 @@ that hosts all other app views. Only the _root module_ should set this `bootstra
Here's a simple root module: Here's a simple root module:
<code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false">
</code-example> </code-example>
@ -114,6 +118,7 @@ Launch an application by _bootstrapping_ its root module.
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one. During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
<code-example path="architecture/src/main.ts" linenums="false"> <code-example path="architecture/src/main.ts" linenums="false">
</code-example> </code-example>
@ -130,12 +135,14 @@ The module declares some objects to be public by marking them with the `export`
Other JavaScript modules use *import statements* to access public objects from other modules. Other JavaScript modules use *import statements* to access public objects from other modules.
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false"> <code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false">
</code-example> </code-example>
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"> <code-example path="architecture/src/app/app.module.ts" region="export" linenums="false">
</code-example> </code-example>
@ -151,6 +158,7 @@ Other JavaScript modules use *import statements* to access public objects from o
These are two different and _complementary_ module systems. Use them both to write your apps. These are two different and _complementary_ module systems. Use them both to write your apps.
### Angular libraries ### Angular libraries
<figure> <figure>
<img src="assets/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> </figure>
@ -165,18 +173,21 @@ You install them with the **npm** package manager and import parts of them with
For example, import Angular's `Component` decorator from the `@angular/core` library like this: For example, import Angular's `Component` decorator from the `@angular/core` library like this:
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"> <code-example path="architecture/src/app/app.component.ts" region="import" linenums="false">
</code-example> </code-example>
You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements: You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false">
</code-example> </code-example>
In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this. In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false">
</code-example> </code-example>
@ -195,6 +206,7 @@ Learn more from the [Angular modules](guide/ngmodule) page.
~~~ ~~~
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -202,6 +214,7 @@ Learn more from the [Angular modules](guide/ngmodule) page.
## Components ## Components
<figure> <figure>
<img src="assets/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> </figure>
@ -223,6 +236,7 @@ that it acquires from a service.
`HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. `HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class">
</code-example> </code-example>
@ -230,12 +244,14 @@ that it acquires from a service.
Angular creates, updates, and destroys components as the user moves through the application. Angular creates, updates, and destroys components as the user moves through the application.
Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()` declared above. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()` declared above.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
## Templates ## Templates
<figure> <figure>
<img src="assets/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> </figure>
@ -247,6 +263,7 @@ A template looks like regular HTML, except for a few differences. Here is a
template for our `HeroListComponent`: template for our `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.html"> <code-example path="architecture/src/app/hero-list.component.html">
</code-example> </code-example>
@ -261,18 +278,21 @@ The `HeroDetailComponent` (code not shown) presents facts about a particular her
hero that the user selects from the list presented by the `HeroListComponent`. hero that the user selects from the list presented by the `HeroListComponent`.
The `HeroDetailComponent` is a **child** of the `HeroListComponent`. The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
<figure> <figure>
<img src="assets/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> </figure>
Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts. Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
<br class="l-clear-both"> <br class="l-clear-both">
<div class='l-hr'> <div class='l-hr'>
</div> </div>
## Metadata ## Metadata
<figure> <figure>
<img src="assets/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> </figure>
@ -289,6 +309,7 @@ In !{_Lang}, you attach metadata by using !{_a} **!{_decorator}**.
Here's some metadata for `HeroListComponent`: Here's some metadata for `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata">
</code-example> </code-example>
@ -311,6 +332,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
This is one way to tell Angular that the component's constructor requires a `HeroService` This is one way to tell Angular that the component's constructor requires a `HeroService`
so it can get the list of heroes to display. so it can get the list of heroes to display.
<figure> <figure>
<img src="assets/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> </figure>
@ -323,6 +345,7 @@ Apply other metadata !{_decorator}s in a similar fashion to guide Angular behavi
`@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s.<br class="l-clear-both">The architectural takeaway is that you must add metadata to your code `@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s.<br class="l-clear-both">The architectural takeaway is that you must add metadata to your code
so that Angular knows what to do. so that Angular knows what to do.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -332,6 +355,7 @@ so that Angular knows what to do.
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to 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. read as any experienced jQuery programmer can attest.
<figure> <figure>
<img src="assets/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> </figure>
@ -343,6 +367,7 @@ Add binding markup to the template HTML to tell Angular how to connect both side
As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms: As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding"> <code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding">
</code-example> </code-example>
@ -360,6 +385,7 @@ that combines property and event binding in a single notation, using the `ngMode
Here's an example from the `HeroDetailComponent` template: Here's an example from the `HeroDetailComponent` template:
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"> <code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example> </code-example>
@ -371,23 +397,27 @@ as with event binding.
Angular processes *all* data bindings once per JavaScript event cycle, Angular processes *all* data bindings once per JavaScript event cycle,
from the root of the application component tree through all child components. from the root of the application component tree through all child components.
<figure> <figure>
<img src="assets/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> </figure>
Data binding plays an important role in communication Data binding plays an important role in communication
between a template and its component.<br class="l-clear-both"> between a template and its component.<br class="l-clear-both">
<figure> <figure>
<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> <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> </figure>
Data binding is also important for communication between parent and child components.<br class="l-clear-both"> Data binding is also important for communication between parent and child components.<br class="l-clear-both">
<div class='l-hr'> <div class='l-hr'>
</div> </div>
## Directives ## Directives
<figure> <figure>
<img src="assets/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> </figure>
@ -418,6 +448,7 @@ sometimes by name but more often as the target of an assignment or a binding.
The [example template](guide/architecture#templates) uses two built-in structural directives: The [example template](guide/architecture#templates) uses two built-in structural directives:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural"> <code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural">
</code-example> </code-example>
@ -434,6 +465,7 @@ an existing element (typically an `<input>`)
by setting its display value property and responding to change events. by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"> <code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example> </code-example>
@ -447,12 +479,14 @@ Of course, you can also write your own directives. Components such as
`HeroListComponent` are one kind of custom directive. `HeroListComponent` are one kind of custom directive.
<!-- PENDING: link to where to learn more about other kinds! --> <!-- PENDING: link to where to learn more about other kinds! -->
<div class='l-hr'> <div class='l-hr'>
</div> </div>
## Services ## Services
<figure> <figure>
<img src="assets/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> </figure>
@ -475,6 +509,7 @@ Yet services are fundamental to any Angular application. Components are big cons
Here's an example of a service class that logs to the browser console: Here's an example of a service class that logs to the browser console:
<code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class"> <code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class">
</code-example> </code-example>
@ -483,6 +518,7 @@ Here's a `HeroService` that uses a !{_PromiseLinked} to fetch heroes.
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work. The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class"> <code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class">
</code-example> </code-example>
@ -504,12 +540,14 @@ It won't complain if you write a "kitchen sink" component with 3000 lines.
Angular does help you *follow* these principles by making it easy to factor your Angular does help you *follow* these principles by making it easy to factor your
application logic into services and make those services available to components through *dependency injection*. application logic into services and make those services available to components through *dependency injection*.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
## Dependency injection ## Dependency injection
<figure> <figure>
<img src="assets/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> </figure>
@ -520,6 +558,7 @@ Angular uses dependency injection to provide new components with the services th
For example, the constructor of your `HeroListComponent` needs a `HeroService`: For example, the constructor of your `HeroListComponent` needs a `HeroService`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor">
</code-example> </code-example>
@ -535,6 +574,7 @@ Angular can call the component's constructor with those services as arguments.
This is *dependency injection*. This is *dependency injection*.
The process of `HeroService` injection looks a bit like this: The process of `HeroService` injection looks a bit like this:
<figure> <figure>
<img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"> </img> <img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"> </img>
</figure> </figure>
@ -550,6 +590,7 @@ In general, add providers to the [root module](guide/architecture#module) so tha
the same instance of a service is available everywhere. the same instance of a service is available everywhere.
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers"> <code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers">
</code-example> </code-example>
@ -557,6 +598,7 @@ the same instance of a service is available everywhere.
Alternatively, register at a component level in the `providers` property of the `@Component` metadata: Alternatively, register at a component level in the `providers` property of the `@Component` metadata:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers">
</code-example> </code-example>
@ -579,6 +621,7 @@ Points to remember about dependency injection:
* Register *providers* with injectors. * Register *providers* with injectors.
<div class='l-hr'> <div class='l-hr'>
</div> </div>

View File

@ -51,6 +51,7 @@ directive to set an element's background color
when the user hovers over that element. You can apply it like this: when the user hovers over that element. You can apply it like this:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied">
</code-example> </code-example>
@ -63,6 +64,7 @@ named <span ngio-ex>attribute-directives</span>.
Create the following source file in the indicated folder: Create the following source file in the indicated folder:
<code-example path="attribute-directives/src/app/highlight.directive.1.ts"> <code-example path="attribute-directives/src/app/highlight.directive.1.ts">
</code-example> </code-example>
@ -120,6 +122,7 @@ Put the template in its own <span ngio-ex>app.component.html</span>
file that looks like this: file that looks like this:
<code-example path="attribute-directives/src/app/app.component.1.html"> <code-example path="attribute-directives/src/app/app.component.1.html">
</code-example> </code-example>
@ -127,6 +130,7 @@ file that looks like this:
Now reference this template in the `AppComponent`: Now reference this template in the `AppComponent`:
<code-example path="attribute-directives/src/app/app.component.ts"> <code-example path="attribute-directives/src/app/app.component.ts">
</code-example> </code-example>
@ -136,12 +140,14 @@ add that class to the `declarations` NgModule metadata. This way Angular
recognizes the directive when it encounters `myHighlight` in the template. recognizes the directive when it encounters `myHighlight` in the template.
<code-example path="attribute-directives/src/app/app.module.ts"> <code-example path="attribute-directives/src/app/app.module.ts">
</code-example> </code-example>
Now when the app runs, the `myHighlight` directive highlights the paragraph text. Now when the app runs, the `myHighlight` directive highlights the paragraph text.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -156,6 +162,7 @@ Did you remember to add the directive to the `declarations` attribute of `@NgMod
It is easy to forget! It is easy to forget!
Open the console in the browser tools and look for an error like this: Open the console in the browser tools and look for an error like this:
<code-example format="nocode"> <code-example format="nocode">
EXCEPTION: Template parse errors: EXCEPTION: Template parse errors:
Can't bind to 'myHighlight' since it isn't a known property of 'p'. Can't bind to 'myHighlight' since it isn't a known property of 'p'.
@ -186,6 +193,7 @@ Begin by adding `HostListener` to the list of imported symbols;
add the `Input` symbol as well because you'll need it soon. add the `Input` symbol as well because you'll need it soon.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports">
</code-example> </code-example>
@ -194,6 +202,7 @@ Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` !{_decorator}. each adorned by the `HostListener` !{_decorator}.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods">
</code-example> </code-example>
@ -218,6 +227,7 @@ The handlers delegate to a helper method that sets the color on the DOM element,
which you declare and initialize in the constructor. which you declare and initialize in the constructor.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor">
</code-example> </code-example>
@ -225,6 +235,7 @@ which you declare and initialize in the constructor.
Here's the updated directive in full: Here's the updated directive in full:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts">
</code-example> </code-example>
@ -232,6 +243,7 @@ Here's the updated directive in full:
Run the app and confirm that the background color appears when Run the app and confirm that the background color appears when
the mouse hovers over the `p` and disappears as it moves out. the mouse hovers over the `p` and disappears as it moves out.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -245,6 +257,7 @@ In this section, you give the developer the power to set the highlight color whi
Start by adding a `highlightColor` property to the directive class like this: Start by adding a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color">
</code-example> </code-example>
@ -262,6 +275,7 @@ Without that input metadata, Angular rejects the binding; see [below](guide/attr
Try it by adding the following directive binding variations to the `AppComponent` template: Try it by adding the following directive binding variations to the `AppComponent` template:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-1"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-1">
</code-example> </code-example>
@ -269,6 +283,7 @@ Try it by adding the following directive binding variations to the `AppComponent
Add a `color` property to the `AppComponent`. Add a `color` property to the `AppComponent`.
<code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example> </code-example>
@ -276,6 +291,7 @@ Add a `color` property to the `AppComponent`.
Let it control the highlight color with a property binding. Let it control the highlight color with a property binding.
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-2"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-2">
</code-example> </code-example>
@ -283,6 +299,7 @@ Let it control the highlight color with a property binding.
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this. That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>
@ -295,6 +312,7 @@ That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name. You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2">
</code-example> </code-example>
@ -310,6 +328,7 @@ Fortunately you can name the directive property whatever you want _and_ **_alias
Restore the original property name and specify the selector as the alias in the argument to `@Input`. Restore the original property name and specify the selector as the alias in the argument to `@Input`.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color property with alias)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color property with alias)" region="color">
</code-example> </code-example>
@ -320,6 +339,7 @@ _Outside_ the directive, where you bind to it, it's known as `myHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want: You get the best of both worlds: the property name you want and the binding syntax you want:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>
@ -328,6 +348,7 @@ Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method
If someone neglects to bind to `highlightColor`, highlight in red: If someone neglects to bind to `highlightColor`, highlight in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"> <code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter">
</code-example> </code-example>
@ -335,6 +356,7 @@ If someone neglects to bind to `highlightColor`, highlight in red:
Here's the latest version of the directive class. Here's the latest version of the directive class.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (excerpt)"> <code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (excerpt)">
</code-example> </code-example>
@ -348,6 +370,7 @@ lets you pick the highlight color with a radio button and bind your color choice
Update <span ngio-ex>app.component.html</span> as follows: Update <span ngio-ex>app.component.html</span> as follows:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (v2)" region="v2"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (v2)" region="v2">
</code-example> </code-example>
@ -355,12 +378,14 @@ Update <span ngio-ex>app.component.html</span> as follows:
Revise the `AppComponent.color` so that it has no initial value. Revise the `AppComponent.color` so that it has no initial value.
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="attribute-directives/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example> </code-example>
Here are the harness and directive in action. Here are the harness and directive in action.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -377,6 +402,7 @@ Let the template developer set the default color.
Add a second **input** property to `HighlightDirective` called `defaultColor`: Add a second **input** property to `HighlightDirective` called `defaultColor`:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (defaultColor)" region="defaultColor">
</code-example> </code-example>
@ -385,6 +411,7 @@ Revise the directive's `onMouseEnter` so that it first tries to highlight with t
then with the `defaultColor`, and falls back to "red" if both properties are undefined. then with the `defaultColor`, and falls back to "red" if both properties are undefined.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter">
</code-example> </code-example>
@ -396,6 +423,7 @@ The developer should be able to write the following template HTML to both bind t
and fall back to "violet" as the default color. and fall back to "violet" as the default color.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (defaultColor)" region="defaultColor"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (defaultColor)" region="defaultColor">
</code-example> </code-example>
@ -405,6 +433,7 @@ because you made it _public_ with the `@Input` !{_decorator}.
Here's how the harness should work when you're done coding. Here's how the harness should work when you're done coding.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -422,33 +451,40 @@ This page covered how to:
The final source code follows: The final source code follows:
<code-tabs> <code-tabs>
<code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts"> <code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html"> <code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts"> <code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts">
</code-pane> </code-pane>
<code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts"> <code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="attribute-directives/src/main.ts"> <code-pane title="main.ts" path="attribute-directives/src/main.ts">
</code-pane> </code-pane>
<code-pane title="index.html" path="attribute-directives/src/index.html"> <code-pane title="index.html" path="attribute-directives/src/index.html">
</code-pane> </code-pane>
@ -464,6 +500,7 @@ In this demo, the `hightlightColor` property is an ***input*** property of
the `HighlightDirective`. You've seen it applied without an alias: the `HighlightDirective`. You've seen it applied without an alias:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color">
</code-example> </code-example>
@ -471,6 +508,7 @@ the `HighlightDirective`. You've seen it applied without an alias:
You've seen it with an alias: You've seen it with an alias:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color">
</code-example> </code-example>
@ -506,6 +544,7 @@ You can tell if `@Input` is needed by the position of the property name in a bin
Now apply that reasoning to the following example: Now apply that reasoning to the following example:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>

View File

@ -8,45 +8,55 @@ Browser support and polyfills guide.
Angular supports most recent browsers. This includes the following specific versions: Angular supports most recent browsers. This includes the following specific versions:
<table> <table>
<tr> <tr>
<th> <th>
Chrome Chrome
</th> </th>
<th> <th>
Firefox Firefox
</th> </th>
<th> <th>
Edge Edge
</th> </th>
<th> <th>
IE IE
</th> </th>
<th> <th>
Safari Safari
</th> </th>
<th> <th>
iOS iOS
</th> </th>
<th> <th>
Android Android
</th> </th>
<th> <th>
IE mobile IE mobile
</th> </th>
@ -55,43 +65,52 @@ Angular supports most recent browsers. This includes the following specific vers
</tr> </tr>
<tr> <tr>
<td> <td>
latest latest
</td> </td>
<td> <td>
latest latest
</td> </td>
<td> <td>
14 14
</td> </td>
<td> <td>
11 11
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
Marshmallow (6.0) Marshmallow (6.0)
</td> </td>
<td> <td>
11 11
</td> </td>
@ -100,43 +119,52 @@ Angular supports most recent browsers. This includes the following specific vers
</tr> </tr>
<tr> <tr>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
13 13
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
9 9
</td> </td>
<td> <td>
9 9
</td> </td>
<td> <td>
Lollipop<br>(5.0, 5.1) Lollipop<br>(5.0, 5.1)
</td> </td>
<td> <td>
</td> </td>
@ -145,43 +173,52 @@ Angular supports most recent browsers. This includes the following specific vers
</tr> </tr>
<tr> <tr>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
9 9
</td> </td>
<td> <td>
8 8
</td> </td>
<td> <td>
8 8
</td> </td>
<td> <td>
KitKat<br>(4.4) KitKat<br>(4.4)
</td> </td>
<td> <td>
</td> </td>
@ -190,43 +227,52 @@ Angular supports most recent browsers. This includes the following specific vers
</tr> </tr>
<tr> <tr>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
7 7
</td> </td>
<td> <td>
7 7
</td> </td>
<td> <td>
Jelly Bean<br>(4.1, 4.2, 4.3) Jelly Bean<br>(4.1, 4.2, 4.3)
</td> </td>
<td> <td>
</td> </td>
@ -255,6 +301,7 @@ Targeting such a wide range of browsers is challenging because they do not suppo
You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`) You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
that implement missing features in JavaScript. that implement missing features in JavaScript.
<code-example path="quickstart/src/index.html" region="polyfills" linenums="false"> <code-example path="quickstart/src/index.html" region="polyfills" linenums="false">
</code-example> </code-example>
@ -277,15 +324,19 @@ Note that polyfills cannot magically transform an old, slow browser into a moder
### Mandatory polyfills ## ### Mandatory polyfills ##
These are the polyfills required to run an Angular application on each supported browser: These are the polyfills required to run an Angular application on each supported browser:
<table> <table>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<th> <th>
Browsers (desktop & mobile) Browsers (desktop & mobile)
</th> </th>
<th> <th>
Polyfills required Polyfills required
</th> </th>
@ -294,13 +345,16 @@ These are the polyfills required to run an Angular application on each supported
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
Chrome, Firefox, Edge, Safari 9+ Chrome, Firefox, Edge, Safari 9+
</td> </td>
<td> <td>
None None
</td> </td>
@ -309,13 +363,16 @@ These are the polyfills required to run an Angular application on each supported
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
Safari 7 & 8, IE10 & 11, Android 4.1+ Safari 7 & 8, IE10 & 11, Android 4.1+
</td> </td>
<td> <td>
[ES6](guide/browser-support#core-es6) [ES6](guide/browser-support#core-es6)
</td> </td>
@ -324,13 +381,16 @@ These are the polyfills required to run an Angular application on each supported
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
IE9 IE9
</td> </td>
<td> <td>
[ES6<br>classList](guide/browser-support#classlist) [ES6<br>classList](guide/browser-support#classlist)
@ -350,20 +410,25 @@ You'll need a polyfill to use animations in other browsers.
Here are the features which may require additional polyfills: Here are the features which may require additional polyfills:
<table> <table>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<th> <th>
Feature Feature
</th> </th>
<th> <th>
Polyfill Polyfill
</th> </th>
<th style="width: 50%"> <th style="width: 50%">
Browsers (desktop & mobile) Browsers (desktop & mobile)
</th> </th>
@ -372,18 +437,22 @@ Here are the features which may require additional polyfills:
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="./animations.html"> Animations </a> <a href="./animations.html"> Animations </a>
</td> </td>
<td> <td>
[Web Animations](guide/browser-support#web-animations) [Web Animations](guide/browser-support#web-animations)
</td> </td>
<td> <td>
All but Chrome and Firefox<br>Not supported in IE9 All but Chrome and Firefox<br>Not supported in IE9
</td> </td>
@ -392,18 +461,22 @@ Here are the features which may require additional polyfills:
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="../api/common/index/DatePipe-pipe.html" target="_blank"> Date </a> <span> , </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank"> currency </a> <span> , </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank"> decimal </a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank"> percent </a> <span> pipes </span> <a href="../api/common/index/DatePipe-pipe.html" target="_blank"> Date </a> <span> , </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank"> currency </a> <span> , </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank"> decimal </a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank"> percent </a> <span> pipes </span>
</td> </td>
<td> <td>
[Intl API](guide/browser-support#intl) [Intl API](guide/browser-support#intl)
</td> </td>
<td> <td>
All but Chrome, Firefox, Edge, IE11 and Safari 10 All but Chrome, Firefox, Edge, IE11 and Safari 10
</td> </td>
@ -412,18 +485,22 @@ Here are the features which may require additional polyfills:
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="../api/common/index/NgClass-directive.html" target="_blank"> NgClass </a> <span> on SVG elements </span> <a href="../api/common/index/NgClass-directive.html" target="_blank"> NgClass </a> <span> on SVG elements </span>
</td> </td>
<td> <td>
[classList](guide/browser-support#classlist) [classList](guide/browser-support#classlist)
</td> </td>
<td> <td>
IE10, IE11 IE10, IE11
</td> </td>
@ -432,18 +509,22 @@ Here are the features which may require additional polyfills:
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="./server-communication.html"> Http </a> <span> when sending and receiving binary data </span> <a href="./server-communication.html"> Http </a> <span> when sending and receiving binary data </span>
</td> </td>
<td> <td>
[Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata) [Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
</td> </td>
<td> <td>
IE 9 IE 9
</td> </td>
@ -457,20 +538,25 @@ Here are the features which may require additional polyfills:
### Suggested polyfills ## ### Suggested polyfills ##
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application. Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
<table> <table>
<tr> <tr>
<th> <th>
Polyfill Polyfill
</th> </th>
<th> <th>
License License
</th> </th>
<th> <th>
Size* Size*
</th> </th>
@ -479,18 +565,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank"> ES6 </a> <a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank"> ES6 </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
27.4KB 27.4KB
</td> </td>
@ -499,18 +589,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank"> classList </a> <a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank"> classList </a>
</td> </td>
<td> <td>
Public domain Public domain
</td> </td>
<td> <td>
1KB 1KB
</td> </td>
@ -519,18 +613,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank"> Intl </a> <a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank"> Intl </a>
</td> </td>
<td> <td>
MIT / Unicode license MIT / Unicode license
</td> </td>
<td> <td>
13.5KB 13.5KB
</td> </td>
@ -539,18 +637,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank"> Web Animations </a> <a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank"> Web Animations </a>
</td> </td>
<td> <td>
Apache Apache
</td> </td>
<td> <td>
14.8KB 14.8KB
</td> </td>
@ -559,18 +661,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank"> Typed Array </a> <a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank"> Typed Array </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
4KB 4KB
</td> </td>
@ -579,18 +685,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank"> Blob </a> <a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank"> Blob </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
1.3KB 1.3KB
</td> </td>
@ -599,18 +709,22 @@ Below are the polyfills which are used to test the framework itself. They are a
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank"> FormData </a> <a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank"> FormData </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
0.4KB 0.4KB
</td> </td>

View File

@ -55,6 +55,7 @@ In the following example, we import and register several services
in the `@Component` metadata `providers` array. in the `@Component` metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false">
</code-example> </code-example>
@ -74,12 +75,14 @@ Learn more about providers [below](guide/cb-dependency-injection#providers).
Now that we've registered these services, Now that we've registered these services,
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -96,6 +99,7 @@ We see an example of the second case here, where we configure the Component Rout
in the `providers` list of the `AppModule`. in the `providers` list of the `AppModule`.
<code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -118,6 +122,7 @@ At each step, the consumer of dependencies simply declares what it requires in i
For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -126,6 +131,7 @@ The `UserContext` in turn has dependencies on both the `LoggerService` (again) a
a `UserService` that gathers information about a particular user. a `UserService` that gathers information about a particular user.
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false">
</code-example> </code-example>
@ -140,6 +146,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: Once all the dependencies are in place, the `AppComponent` displays the user information:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -147,6 +154,7 @@ Once all the dependencies are in place, the `AppComponent` displays the user inf
### *@Injectable()* ### *@Injectable()*
Notice the `@Injectable()`decorator on the `UserContextService` class. Notice the `@Injectable()`decorator on the `UserContextService` class.
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectable" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectable" linenums="false">
</code-example> </code-example>
@ -203,6 +211,7 @@ We can limit the scope of an injected service to a *branch* of the application h
by providing that service *at the sub-root component for that branch*. by providing that service *at the sub-root component for that branch*.
Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array: Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array:
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="injection"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="injection">
</code-example> </code-example>
@ -247,6 +256,7 @@ We call this *sandboxing* because each service and component instance has its ow
<a id="hero-bios-component"></a> <a id="hero-bios-component"></a>
Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple">
</code-example> </code-example>
@ -254,6 +264,7 @@ Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioCompo
Each `HeroBioComponent` can edit a single hero's biography. Each `HeroBioComponent` can edit a single hero's biography.
A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero. A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero.
<code-example path="cb-dependency-injection/src/app/hero-cache.service.ts" region="service"> <code-example path="cb-dependency-injection/src/app/hero-cache.service.ts" region="service">
</code-example> </code-example>
@ -264,6 +275,7 @@ They'd be competing with each other to determine which hero to cache.
Each `HeroBioComponent` gets its *own* `HeroCacheService` instance Each `HeroBioComponent` gets its *own* `HeroCacheService` instance
by listing the `HeroCacheService` in its metadata `providers` array. by listing the `HeroCacheService` in its metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component"> <code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component">
</code-example> </code-example>
@ -275,6 +287,7 @@ And the template displays this data-bound property.
Find this example in <live-example name="cb-dependency-injection">live code</live-example> 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. and confirm that the three `HeroBioComponent` instances have their own cached hero data.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img> <img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img>
</figure> </figure>
@ -309,12 +322,14 @@ We look at this second, more interesting case in our next example.
### Demonstration ### Demonstration
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component). The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component).
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts">
</code-example> </code-example>
Focus on the template: Focus on the template:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -323,23 +338,27 @@ We've inserted a `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template: placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="template" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="template" linenums="false">
</code-example> </code-example>
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description: It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component">
</code-example> </code-example>
Focus on the constructor parameters Focus on the constructor parameters
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false">
</code-example> </code-example>
@ -363,6 +382,7 @@ We'll come back to the `elementRef` property shortly.
~~~ ~~~
Here's the `HeroBiosAndContactsComponent` in action. Here's the `HeroBiosAndContactsComponent` in action.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -370,6 +390,7 @@ Here's the `HeroBiosAndContactsComponent` in action.
If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree 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 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. with the gratuitous "!!!", indicating that the logger was found.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -387,6 +408,7 @@ require DOM access.
To illustrate, we've written a simplified version of the `HighlightDirective` from To illustrate, we've written a simplified version of the `HighlightDirective` from
the [Attribute Directives](guide/attribute-directives) chapter. the [Attribute Directives](guide/attribute-directives) chapter.
<code-example path="cb-dependency-injection/src/app/highlight.directive.ts"> <code-example path="cb-dependency-injection/src/app/highlight.directive.ts">
</code-example> </code-example>
@ -401,11 +423,13 @@ Its `nativeElement` property exposes the DOM element for the directive to manipu
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags, The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
first without a value (yielding the default color) and then with an assigned color value. first without a value (yielding the default color) and then with an assigned color value.
<code-example path="cb-dependency-injection/src/app/app.component.html" region="highlight" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.html" region="highlight" linenums="false">
</code-example> </code-example>
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag. The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios"> </img> <img src="assets/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios"> </img>
</figure> </figure>
@ -424,6 +448,7 @@ Angular passes this token to the injector and assigns the result to the paramete
Here's a typical example: Here's a typical example:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -451,6 +476,7 @@ Angular initializes the injectors it creates with some providers it cares about.
We have to register our _own_ application providers manually, We have to register our _own_ application providers manually,
usually in the `providers` array of the `Component` or `Directive` metadata: usually in the `providers` array of the `Component` or `Directive` metadata:
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="providers"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="providers">
</code-example> </code-example>
@ -460,6 +486,7 @@ usually in the `providers` array of the `Component` or `Directive` metadata:
The simple class provider is the most typical by far. The simple class provider is the most typical by far.
We mention the class in the `providers` array and we're done. We mention the class in the `providers` array and we're done.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="class-provider" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="class-provider" linenums="false">
</code-example> </code-example>
@ -470,12 +497,14 @@ 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. The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about. It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month">
</code-example> </code-example>
@ -506,6 +535,7 @@ The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class; The first provides an instance of the `Hero` class;
the second specifies a literal string resource: the second specifies a literal string resource:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-value" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-value" linenums="false">
</code-example> </code-example>
@ -521,6 +551,7 @@ The value of a *value provider* must be defined *now*. We can't create the value
Obviously the title string literal is immediately available. Obviously the title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file: The `someHero` variable in this example was set earlier in the file:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="some-hero"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="some-hero">
</code-example> </code-example>
@ -540,6 +571,7 @@ or fake the behavior of the real class in a test case.
We see two examples in the `HeroOfTheMonthComponent`: We see two examples in the `HeroOfTheMonthComponent`:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-class" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-class" linenums="false">
</code-example> </code-example>
@ -561,6 +593,7 @@ Components outside the tree continue to receive the original `LoggerService` ins
The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false">
</code-example> </code-example>
@ -575,6 +608,7 @@ The `useExisting` provider maps one token to another.
In effect, the first token is an ***alias*** for the service associated with second token, In effect, the first token is an ***alias*** for the service associated with second token,
creating ***two ways to access the same service object***. creating ***two ways to access the same service object***.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example> </code-example>
@ -585,11 +619,13 @@ Imagine that the `LoggerService` had a large API (it's actually only three metho
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface): We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface):
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example> </code-example>
The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript: The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -597,6 +633,7 @@ The constructor's `logger` parameter is typed as `MinimalLogger` so only its two
Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` 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`. 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: The following image, which displays the logging date, confirms the point:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -610,6 +647,7 @@ The following image, which displays the logging date, confirms the point:
The `useFactory` provider creates a dependency object by calling a factory function The `useFactory` provider creates a dependency object by calling a factory function
as seen in this example. as seen in this example.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory">
</code-example> </code-example>
@ -628,6 +666,7 @@ The `runnersUpFactory` itself isn't the provider factory function.
The true provider factory function is the function that `runnersUpFactory` returns. The true provider factory function is the function that `runnersUpFactory` returns.
<code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false"> <code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false">
</code-example> </code-example>
@ -671,12 +710,14 @@ That's the subject of our next section.
In the previous *Hero of the Month* example, we used the `MinimalLogger` class In the previous *Hero of the Month* example, we used the `MinimalLogger` class
as the token for a provider of a `LoggerService`. as the token for a provider of a `LoggerService`.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example> </code-example>
The `MinimalLogger` is an abstract class. The `MinimalLogger` is an abstract class.
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example> </code-example>
@ -687,6 +728,7 @@ Instead, we use it like an interface.
Look again at the declaration for `DateLoggerService` Look again at the declaration for `DateLoggerService`
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service-signature" linenums="false">
</code-example> </code-example>
@ -719,6 +761,7 @@ Using a class as an interface gives us the characteristics of an interface in a
The minimize memory cost, the class should have *no implementation*. The minimize memory cost, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger-transpiled" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger-transpiled" linenums="false">
</code-example> </code-example>
@ -745,12 +788,14 @@ The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example, We encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider. in the *title* value provider and in the *runnersUp* factory provider.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false">
</code-example> </code-example>
We created the `TITLE` token like this: We created the `TITLE` token like this:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false">
</code-example> </code-example>
@ -768,6 +813,7 @@ and then pass them down to the base class through the constructor.
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
to display a *sorted* list of heroes. to display a *sorted* list of heroes.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -777,6 +823,7 @@ It demands its own instance of the `HeroService` to get heroes
and displays them in the order they arrive from the database. and displays them in the order they arrive from the database.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base">
</code-example> </code-example>
@ -804,6 +851,7 @@ We must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor. then pass it down to the base class inside the constructor.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes">
</code-example> </code-example>
@ -850,6 +898,7 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex} {@a alex}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false">
</code-example> </code-example>
@ -857,6 +906,7 @@ In the following example, the parent `AlexComponent` has several children includ
*Cathy* reports whether or not she has access to *Alex* *Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor: after injecting an `AlexComponent` into her constructor:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="cathy" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="cathy" linenums="false">
</code-example> </code-example>
@ -894,12 +944,14 @@ We are asking *can a component inject its parent via the parent's base class*?
The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex) The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex)
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`. we see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example> </code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="craig" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="craig" linenums="false">
</code-example> </code-example>
@ -926,6 +978,7 @@ and add that provider to the `providers` array of the `@Component` metadata for
{@a alex-providers} {@a alex-providers}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>
@ -935,11 +988,13 @@ The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false">
</code-example> </code-example>
Here's *Alex* and family in action: Here's *Alex* and family in action:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -958,6 +1013,7 @@ That means he must both *inject* the `Parent` *class-interface* to get *Alice* a
Here's *Barry*: Here's *Barry*:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false">
</code-example> </code-example>
@ -967,13 +1023,16 @@ If we're going to keep writing [*alias providers*](guide/cb-dependency-injection
For now, focus on *Barry*'s constructor: For now, focus on *Barry*'s constructor:
<code-tabs> <code-tabs>
<code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor"> <code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor">
</code-pane> </code-pane>
<code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor"> <code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor">
</code-pane> </code-pane>
@ -994,6 +1053,7 @@ which *is* what parent means.
Here's *Alice*, *Barry* and family in action: Here's *Alice*, *Barry* and family in action:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -1006,6 +1066,7 @@ We [learned earlier](guide/cb-dependency-injection#class-interface) that a *clas
Our example defines a `Parent` *class-interface* . Our example defines a `Parent` *class-interface* .
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="parent" linenums="false">
</code-example> </code-example>
@ -1016,6 +1077,7 @@ Such a narrowing interface helps decouple the child component class from its par
A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false">
</code-example> </code-example>
@ -1024,6 +1086,7 @@ Doing so adds clarity to the code. But it's not technically necessary.
Although the `AlexComponent` has a `name` property (as required by its `Base` class) Although the `AlexComponent` has a `name` property (as required by its `Base` class)
its class signature doesn't mention `Parent`: its class signature doesn't mention `Parent`:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example> </code-example>
@ -1046,18 +1109,21 @@ It doesn't in this example *only* to demonstrate that the code will compile and
Writing variations of the same parent *alias provider* gets old quickly, Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref): especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref):
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>
We can extract that logic into a helper function like this: We can extract that logic into a helper function like this:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-the-parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-the-parent" linenums="false">
</code-example> </code-example>
Now we can add a simpler, more meaningful parent provider to our components: Now we can add a simpler, more meaningful parent provider to our components:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-providers" linenums="false">
</code-example> </code-example>
@ -1067,12 +1133,14 @@ Our application might have a variety of parent types, each with its own *class-i
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false">
</code-example> </code-example>
And here's how we could use it with a different parent type: And here's how we could use it with a different parent type:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false">
</code-example> </code-example>
@ -1101,6 +1169,7 @@ appear *above* the class definition.
We break the circularity with `forwardRef`: We break the circularity with `forwardRef`:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>

View File

@ -23,6 +23,7 @@ in which two or more components share information.
typically adorned with [@Input decorations](guide/template-syntax). typically adorned with [@Input decorations](guide/template-syntax).
<code-example path="cb-component-communication/src/app/hero-child.component.ts"> <code-example path="cb-component-communication/src/app/hero-child.component.ts">
</code-example> </code-example>
@ -34,12 +35,14 @@ binding its `master` string property to the child's `master` alias,
and each iteration's `hero` instance to the child's `hero` property. and each iteration's `hero` instance to the child's `hero` property.
<code-example path="cb-component-communication/src/app/hero-parent.component.ts"> <code-example path="cb-component-communication/src/app/hero-parent.component.ts">
</code-example> </code-example>
The running application displays three heroes: The running application displays three heroes:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -49,6 +52,7 @@ The running application displays three heroes:
E2E test that all children were instantiated and displayed as expected: E2E test that all children were instantiated and displayed as expected:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child">
</code-example> </code-example>
@ -63,6 +67,7 @@ The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text. trims the whitespace from a name and replaces an empty value with default text.
<code-example path="cb-component-communication/src/app/name-child.component.ts"> <code-example path="cb-component-communication/src/app/name-child.component.ts">
</code-example> </code-example>
@ -70,11 +75,13 @@ trims the whitespace from a name and replaces an empty value with default text.
Here's the `NameParentComponent` demonstrating name variations including a name with all spaces: Here's the `NameParentComponent` demonstrating name variations including a name with all spaces:
<code-example path="cb-component-communication/src/app/name-parent.component.ts"> <code-example path="cb-component-communication/src/app/name-parent.component.ts">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -84,6 +91,7 @@ Here's the `NameParentComponent` demonstrating name variations including a name
E2E tests of input property setter with empty and non-empty names: E2E tests of input property setter with empty and non-empty names:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter">
</code-example> </code-example>
@ -105,6 +113,7 @@ Learn about `ngOnChanges()` in the [LifeCycle Hooks](guide/lifecycle-hooks) chap
This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes: This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
<code-example path="cb-component-communication/src/app/version-child.component.ts"> <code-example path="cb-component-communication/src/app/version-child.component.ts">
</code-example> </code-example>
@ -112,12 +121,14 @@ This `VersionChildComponent` detects changes to the `major` and `minor` input pr
The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them. The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them.
<code-example path="cb-component-communication/src/app/version-parent.component.ts"> <code-example path="cb-component-communication/src/app/version-parent.component.ts">
</code-example> </code-example>
Here's the output of a button-pushing sequence: Here's the output of a button-pushing sequence:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -128,6 +139,7 @@ Test that ***both*** input properties are set initially and that button clicks t
the expected `ngOnChanges` calls and values: the expected `ngOnChanges` calls and values:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges">
</code-example> </code-example>
@ -144,6 +156,7 @@ The child's `EventEmitter` property is an ***output property***,
as seen in this `VoterComponent`: as seen in this `VoterComponent`:
<code-example path="cb-component-communication/src/app/voter.component.ts"> <code-example path="cb-component-communication/src/app/voter.component.ts">
</code-example> </code-example>
@ -154,6 +167,7 @@ The parent `VoteTakerComponent` binds an event handler called `onVoted()` that r
payload `$event` and updates a counter. payload `$event` and updates a counter.
<code-example path="cb-component-communication/src/app/votetaker.component.ts"> <code-example path="cb-component-communication/src/app/votetaker.component.ts">
</code-example> </code-example>
@ -161,6 +175,7 @@ payload `$event` and updates a counter.
The framework passes the event argument&mdash;represented by `$event`&mdash;to the handler method, The framework passes the event argument&mdash;represented by `$event`&mdash;to the handler method,
and the method processes it: and the method processes it:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -170,6 +185,7 @@ and the method processes it:
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters: Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
<code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent"> <code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent">
</code-example> </code-example>
@ -189,6 +205,7 @@ The following is a child `CountdownTimerComponent` that repeatedly counts down t
It has `start` and `stop` methods that control the clock and it displays a It has `start` and `stop` methods that control the clock and it displays a
countdown status message in its own template. countdown status message in its own template.
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts"> <code-example path="cb-component-communication/src/app/countdown-timer.component.ts">
</code-example> </code-example>
@ -196,6 +213,7 @@ countdown status message in its own template.
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows: The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv"> <code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv">
</code-example> </code-example>
@ -212,6 +230,7 @@ uses interpolation to display the child's `seconds` property.
Here we see the parent and child working together. Here we see the parent and child working together.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -226,6 +245,7 @@ match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer: Test also that clicking the *Stop* button pauses the countdown timer:
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests"> <code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests">
</code-example> </code-example>
@ -258,6 +278,7 @@ is solely for the purpose of demonstration.
Here is the parent, `CountdownViewChildParentComponent`: Here is the parent, `CountdownViewChildParentComponent`:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc"> <code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc">
</code-example> </code-example>
@ -301,6 +322,7 @@ Components outside this component subtree have no access to the service or their
This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children. This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children.
<code-example path="cb-component-communication/src/app/mission.service.ts"> <code-example path="cb-component-communication/src/app/mission.service.ts">
</code-example> </code-example>
@ -309,6 +331,7 @@ The `MissionControlComponent` both provides the instance of the service that it
(through the `providers` metadata array) and injects that instance into itself through its constructor: (through the `providers` metadata array) and injects that instance into itself through its constructor:
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts"> <code-example path="cb-component-communication/src/app/missioncontrol.component.ts">
</code-example> </code-example>
@ -317,6 +340,7 @@ The `AstronautComponent` also injects the service in its constructor.
Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance: Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance:
<code-example path="cb-component-communication/src/app/astronaut.component.ts"> <code-example path="cb-component-communication/src/app/astronaut.component.ts">
</code-example> </code-example>
@ -339,6 +363,7 @@ The *History* log demonstrates that messages travel in both directions between
the parent `MissionControlComponent` and the `AstronautComponent` children, the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service: facilitated by the service:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -349,6 +374,7 @@ Tests click buttons of both the parent `MissionControlComponent` and the `Astron
and verify that the history meets expectations: and verify that the history meets expectations:
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service"> <code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service">
</code-example> </code-example>

View File

@ -37,6 +37,7 @@ The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
Usually you give it one string, as in the following example: Usually you give it one string, as in the following example:
<code-example path="component-styles/src/app/hero-app.component.ts" linenums="false"> <code-example path="component-styles/src/app/hero-app.component.ts" linenums="false">
</code-example> </code-example>
@ -73,6 +74,7 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template). targeting elements *inside* the component's template).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false">
</code-example> </code-example>
@ -87,6 +89,7 @@ including another selector inside parentheses after `:host`.
The next example targets the host element again, but only when it also has the `active` CSS class. The next example targets the host element again, but only when it also has the `active` CSS class.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false">
</code-example> </code-example>
@ -105,6 +108,7 @@ The following example applies a `background-color` style to all `<h2>` elements
if some ancestor element has the CSS class `theme-light`. if some ancestor element has the CSS class `theme-light`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false">
</code-example> </code-example>
@ -120,6 +124,7 @@ children and content children of the component.
The following example targets all `<h3>` elements, from the host element down The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM. through this component to all of its child elements in the DOM.
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false">
</code-example> </code-example>
@ -155,6 +160,7 @@ You can add a `styles` #{_array} property to the `@Component` #{_decorator}.
Each string in the #{_array} (usually just one string) defines the CSS. Each string in the #{_array} (usually just one string) defines the CSS.
<code-example path="component-styles/src/app/hero-app.component.ts"> <code-example path="component-styles/src/app/hero-app.component.ts">
</code-example> </code-example>
@ -165,6 +171,7 @@ You can load styles from external CSS files by adding a `styleUrls` attribute
into a component's `@Component` #{_decorator}: into a component's `@Component` #{_decorator}:
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls"> <code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls">
</code-example> </code-example>
@ -208,6 +215,7 @@ You can embed styles directly into the HTML template by putting them
inside `<style>` tags. inside `<style>` tags.
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles"> <code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles">
</code-example> </code-example>
@ -220,6 +228,7 @@ As with `styleUrls`, the link tag's `href` URL is relative to the
application root, not the component file. application root, not the component file.
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink"> <code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink">
</code-example> </code-example>
@ -233,6 +242,7 @@ on the [MDN](https://developer.mozilla.org) site.
In this case, the URL is relative to the CSS file into which you're importing. In this case, the URL is relative to the CSS file into which you're importing.
<code-example path="component-styles/src/app/hero-details.component.css" region="import"> <code-example path="component-styles/src/app/hero-details.component.css" region="import">
</code-example> </code-example>
@ -266,6 +276,7 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata: To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false"> <code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false">
</code-example> </code-example>
@ -288,6 +299,7 @@ In the DOM of a running Angular application with emulated view
encapsulation enabled, each DOM element has some extra attributes encapsulation enabled, each DOM element has some extra attributes
attached to it: attached to it:
<code-example format=""> <code-example format="">
&lt;hero-details _nghost-pmm-5> &lt;hero-details _nghost-pmm-5>
&lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2> &lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2>
@ -308,6 +320,7 @@ 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 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: by the generated component styles, which are in the `<head>` section of the DOM:
<code-example format=""> <code-example format="">
[_nghost-pmm-5] { [_nghost-pmm-5] {
display: block; display: block;
@ -331,6 +344,7 @@ These extra selectors enable the scoping rules described in this page.
## 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: 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"> <code-example format="nocode">
quest-summary.component.ts quest-summary.component.ts
quest-summary.component.html quest-summary.component.html
@ -345,6 +359,7 @@ it would be nice to refer to them by name without also having to specify a path
You can use a relative URL by prefixing your filenames with `./`: You can use a relative URL by prefixing your filenames with `./`:
<code-example path="component-styles/src/app/quest-summary.component.ts"> <code-example path="component-styles/src/app/quest-summary.component.ts">
</code-example> </code-example>

View File

@ -48,6 +48,7 @@ To understand why dependency injection is so important, consider an example with
Imagine writing the following code: Imagine writing the following code:
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car"> <code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car">
</code-example> </code-example>
@ -98,13 +99,16 @@ How can you make `Car` more robust, flexible, and testable?
That's super easy. Change the `Car` constructor to a version with DI: That's super easy. Change the `Car` constructor to a version with DI:
<code-tabs> <code-tabs>
<code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor"> <code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor">
</code-pane> </code-pane>
<code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor"> <code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor">
</code-pane> </code-pane>
@ -130,6 +134,7 @@ parameters and properties simultaneously.
Now you can create a car by passing the engine and tires to the constructor. Now you can create a car by passing the engine and tires to the constructor.
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false">
</code-example> </code-example>
@ -149,6 +154,7 @@ The _consumer_ of `Car` has the problem. The consumer must update the car creati
something like this: something like this:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
</code-example> </code-example>
@ -165,6 +171,7 @@ You can pass mocks to the constructor that do exactly what you want them to do
during each test: during each test:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
</code-example> </code-example>
@ -183,6 +190,7 @@ You need something that takes care of assembling these parts.
You _could_ write a giant class to do that: You _could_ write a giant class to do that:
<code-example path="dependency-injection/src/app/car/car-factory.ts"> <code-example path="dependency-injection/src/app/car/car-factory.ts">
</code-example> </code-example>
@ -202,6 +210,7 @@ You register some classes with this injector, and it figures out how to create t
When you need a `Car`, you simply ask the injector to get it for you and you're good to go. When you need a `Car`, you simply ask the injector to get it for you and you're good to go.
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false"> <code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false">
</code-example> </code-example>
@ -226,23 +235,28 @@ start with a simplified version of the `HeroesComponent`
that from the [The Tour of Heroes](tutorial/). that from the [The Tour of Heroes](tutorial/).
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="v1"> <code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="v1">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.1.ts"> <code-pane title="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero.ts" path="dependency-injection/src/app/heroes/hero.ts"> <code-pane title="src/app/heroes/hero.ts" path="dependency-injection/src/app/heroes/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/mock-heroes.ts" path="dependency-injection/src/app/heroes/mock-heroes.ts"> <code-pane title="src/app/heroes/mock-heroes.ts" path="dependency-injection/src/app/heroes/mock-heroes.ts">
</code-pane> </code-pane>
@ -276,6 +290,7 @@ The following `HeroService` exposes a `getHeroes` method that returns
the same mock data as before, but none of its consumers need to know that. the same mock data as before, but none of its consumers need to know that.
<code-example path="dependency-injection/src/app/heroes/hero.service.1.ts"> <code-example path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-example> </code-example>
@ -306,6 +321,7 @@ This is important in general, but not in this example.
A service is nothing more than a class in Angular. A service is nothing more than a class in Angular.
It remains nothing more than a class until you register it with an Angular injector. It remains nothing more than a class until you register it with an Angular injector.
<div id='bootstrap'> <div id='bootstrap'>
</div> </div>
@ -319,6 +335,7 @@ You don't have to create an Angular injector.
Angular creates an application-wide injector for you during the bootstrap process. Angular creates an application-wide injector for you during the bootstrap process.
<code-example path="dependency-injection/src/main.ts" linenums="false" title="src/main.ts (bootstrap)" region="bootstrap"> <code-example path="dependency-injection/src/main.ts" linenums="false" title="src/main.ts (bootstrap)" region="bootstrap">
</code-example> </code-example>
@ -336,6 +353,7 @@ Here's the `AppModule` that registers two providers, `UserService` and an `APP_C
in its `providers` !{_array}. in its `providers` !{_array}.
<code-example path="dependency-injection/app_module_ts + ' (excerpt)'" linenums="false" title="app_module_ts + ' (excerpt)' (ngmodule)" region="ngmodule"> <code-example path="dependency-injection/app_module_ts + ' (excerpt)'" linenums="false" title="app_module_ts + ' (excerpt)' (ngmodule)" region="ngmodule">
</code-example> </code-example>
@ -351,6 +369,7 @@ place to register it.
Here's a revised `HeroesComponent` that registers the `HeroService` in its `providers` !{_array}. Here's a revised `HeroesComponent` that registers the `HeroService` in its `providers` !{_array}.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" linenums="false"> <code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" linenums="false">
</code-example> </code-example>
@ -393,13 +412,16 @@ constructor, [as discussed earlier](guide/dependency-injection#ctor-injection).
It's a small change: It's a small change:
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/hero-list.component (with DI)" path="dependency-injection/src/app/heroes/hero-list.component.2.ts"> <code-pane title="src/app/heroes/hero-list.component (with DI)" path="dependency-injection/src/app/heroes/hero-list.component.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero-list.component (without DI)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts"> <code-pane title="src/app/heroes/hero-list.component (without DI)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
</code-pane> </code-pane>
@ -416,6 +438,7 @@ It's a small change:
Adding a parameter to the constructor isn't all that's happening here. Adding a parameter to the constructor isn't all that's happening here.
<code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -445,6 +468,7 @@ You _could_ create such an injector
explicitly: explicitly:
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false"> <code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false">
</code-example> </code-example>
@ -481,6 +505,7 @@ For example, you can create a new `HeroListComponent` with a mock service that y
under test: under test:
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false"> <code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false">
</code-example> </code-example>
@ -509,13 +534,16 @@ adding a constructor that takes a `Logger` parameter.
Here is the revision compared to the original. Here is the revision compared to the original.
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts"> <code-pane title="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts"> <code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-pane> </code-pane>
@ -553,6 +581,7 @@ in order to inject a `Logger`.
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
Suggestion: add @Injectable() to every service class Suggestion: add @Injectable() to every service class
</header> </header>
@ -560,13 +589,16 @@ in order to inject a `Logger`.
Consider adding `@Injectable()` to every service class, even those that don't have dependencies Consider adding `@Injectable()` to every service class, even those that don't have dependencies
and, therefore, do not technically require it. Here's why: and, therefore, do not technically require it. Here's why:
<ul style="font-size:inherit"> <ul style="font-size:inherit">
<li> <li>
<b>Future proofing:</b> No need to remember <code>@Injectable()</code> when you add a dependency later. <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when you add a dependency later.
</li> </li>
<li> <li>
<b>Consistency:</b> All services follow the same rules, and you don't have to wonder why is missing. <b>Consistency:</b> All services follow the same rules, and you don't have to wonder why is missing.
</li> </li>
@ -593,6 +625,7 @@ identify a class as a target for instantiation by an injector.
~~~ {.callout.is-critical} ~~~ {.callout.is-critical}
<header> <header>
Always include the parentheses Always include the parentheses
</header> </header>
@ -614,6 +647,7 @@ Inject a logger into `HeroService` in two steps:
The logger service is quite simple: The logger service is quite simple:
<code-example path="dependency-injection/src/app/logger.service.ts"> <code-example path="dependency-injection/src/app/logger.service.ts">
</code-example> </code-example>
@ -624,11 +658,13 @@ so put it in the project's `#{_appDir}` folder and
register it in the `providers` #{_array} of the application !{_moduleVsComp}, `!{_AppModuleVsAppComp}`. register it in the `providers` #{_array} of the application !{_moduleVsComp}, `!{_AppModuleVsAppComp}`.
<code-example path="dependency-injection/src/app/providers.component.ts" linenums="false" title="src/app/providers.component.ts (excerpt)" region="providers-logger"> <code-example path="dependency-injection/src/app/providers.component.ts" linenums="false" title="src/app/providers.component.ts (excerpt)" region="providers-logger">
</code-example> </code-example>
If you forget to register the logger, Angular throws an exception when it first looks for the logger: If you forget to register the logger, Angular throws an exception when it first looks for the logger:
<code-example format="nocode"> <code-example format="nocode">
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger) EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
@ -652,6 +688,7 @@ You must register a service *provider* with the injector, or it won't know how t
Earlier you registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppModule` like this: Earlier you registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppModule` like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example> </code-example>
@ -667,6 +704,7 @@ Any of these approaches might be a good choice under the right circumstances.
What matters is that the injector has a provider to go to when it needs a `Logger`. What matters is that the injector has a provider to go to when it needs a `Logger`.
<div id='provide'> <div id='provide'>
</div> </div>
@ -675,6 +713,7 @@ What matters is that the injector has a provider to go to when it needs a `Logge
You wrote the `providers` #{_array} like this: You wrote the `providers` #{_array} like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1">
</code-example> </code-example>
@ -684,6 +723,7 @@ This is actually a shorthand expression for a provider registration
using a _provider_ object literal with two properties: using a _provider_ object literal with two properties:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3">
</code-example> </code-example>
@ -696,6 +736,7 @@ The second is a !{_secondParam},
which you can think of as a *recipe* for creating the dependency value. which you can think of as a *recipe* for creating the dependency value.
There are many ways to create dependency values just as there are many ways to write a recipe. There are many ways to create dependency values just as there are many ways to write a recipe.
<div id='class-provider'> <div id='class-provider'>
</div> </div>
@ -707,6 +748,7 @@ The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`. to return a `BetterLogger` when something asks for the `Logger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4">
</code-example> </code-example>
@ -721,6 +763,7 @@ This logger gets the user from the injected `UserService`,
which is also injected at the application level. which is also injected at the application level.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example> </code-example>
@ -728,6 +771,7 @@ which is also injected at the application level.
Configure it like `BetterLogger`. Configure it like `BetterLogger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example> </code-example>
@ -752,6 +796,7 @@ You certainly do not want two different `NewLogger` instances in your app.
Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`. Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example> </code-example>
@ -759,6 +804,7 @@ Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger
The solution: alias with the `useExisting` option. The solution: alias with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example> </code-example>
@ -771,6 +817,7 @@ Sometimes it's easier to provide a ready-made object rather than ask the injecto
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example> </code-example>
@ -779,6 +826,7 @@ Then you register a provider with the `useValue` option,
which makes this object play the logger role. which makes this object play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example> </code-example>
@ -787,6 +835,7 @@ See more `useValue` examples in the
[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and [Non-class dependencies](guide/dependency-injection#non-class-dependencies) and
[OpaqueToken](guide/dependency-injection#opaquetoken) sections. [OpaqueToken](guide/dependency-injection#opaquetoken) sections.
<div id='factory-provider'> <div id='factory-provider'>
</div> </div>
@ -816,6 +865,7 @@ who is authorized and who is not.
Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes. Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false">
</code-example> </code-example>
@ -826,6 +876,7 @@ You'll have to take over the creation of new instances of this `HeroService` wit
A factory provider needs a factory function: A factory provider needs a factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false">
</code-example> </code-example>
@ -836,6 +887,7 @@ You inject both the `Logger` and the `UserService` into the factory provider
and let the injector pass them along to the factory function: and let the injector pass them along to the factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false">
</code-example> </code-example>
@ -863,13 +915,16 @@ where it replaces the previous `HeroService` registration in the metadata `provi
Here you see the new and the old implementation side-by-side: Here you see the new and the old implementation side-by-side:
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts"> <code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full"> <code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full">
</code-pane> </code-pane>
@ -889,6 +944,7 @@ the class *type* served as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false"> <code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false">
</code-example> </code-example>
@ -899,6 +955,7 @@ Angular knows to inject the
service associated with that `HeroService` class token: service associated with that `HeroService` class token:
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature"> <code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature">
</code-example> </code-example>
@ -908,12 +965,14 @@ This is especially convenient when you consider that most dependency values are
{@a non-class-dependencies} {@a non-class-dependencies}
### Non-class dependencies ### Non-class dependencies
<p> <p>
What if the dependency value isn't a class? Sometimes the thing you want to inject is a What if the dependency value isn't a class? Sometimes the thing you want to inject is a
span string, function, or object. span string, function, or object.
</p> </p>
<p> <p>
Applications often define configuration objects with lots of small facts Applications often define configuration objects with lots of small facts
(like the title of the application or the address of a web API endpoint) (like the title of the application or the address of a web API endpoint)
@ -923,6 +982,7 @@ This is especially convenient when you consider that most dependency values are
<code-example path="dependency-injection/src/app/app.config.ts" region="config" linenums="false"> <code-example path="dependency-injection/src/app/app.config.ts" region="config" linenums="false">
</code-example> </code-example>
@ -942,12 +1002,14 @@ There is no `AppConfig` class.
The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you
cannot use a TypeScript interface as a token: cannot use a TypeScript interface as a token:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example> </code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example> </code-example>
@ -972,6 +1034,7 @@ to define and use an !{opaquetoken}.
The definition looks like this: The definition looks like this:
<code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false"> <code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false">
</code-example> </code-example>
@ -979,6 +1042,7 @@ The definition looks like this:
Register the dependency provider using the `OpaqueToken` object: Register the dependency provider using the `OpaqueToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
</code-example> </code-example>
@ -987,6 +1051,7 @@ Now you can inject the configuration object into any constructor that needs it,
the help of an `@Inject` #{_decorator}: the help of an `@Inject` #{_decorator}:
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false"> <code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -1005,11 +1070,13 @@ it supports typing of the configuration object within the class.
Aternatively, you can provide and inject the configuration object in an ngModule like `AppModule`. Aternatively, you can provide and inject the configuration object in an ngModule like `AppModule`.
<code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (ngmodule-providers)" region="ngmodule-providers"> <code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (ngmodule-providers)" region="ngmodule-providers">
</code-example> </code-example>
<div id='optional'> <div id='optional'>
</div> </div>
@ -1022,6 +1089,7 @@ You can tell Angular that the dependency is optional by annotating the
constructor argument with `@Optional()`: constructor argument with `@Optional()`:
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
</code-example> </code-example>
@ -1048,6 +1116,7 @@ Developers rarely work directly with an injector, but
here's an `InjectorComponent` that does. here's an `InjectorComponent` that does.
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector"> <code-example path="dependency-injection/src/app/injector.component.ts" region="injector">
</code-example> </code-example>

View File

@ -108,6 +108,7 @@ Load the few files you need from the web instead.
with versions that load from the web. It might look like this. with versions that load from the web. It might look like this.
<code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false"> <code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false">
</code-example> </code-example>
@ -115,6 +116,7 @@ with versions that load from the web. It might look like this.
(2) Replace the `systemjs.config.js` script with a script that (2) Replace the `systemjs.config.js` script with a script that
loads `systemjs.config.server.js`. loads `systemjs.config.server.js`.
<code-example path="deployment/src/index.html" region="systemjs-config" linenums="false"> <code-example path="deployment/src/index.html" region="systemjs-config" linenums="false">
</code-example> </code-example>
@ -129,6 +131,7 @@ you make to `systemjs.config.js`.
Notice the `paths` key: Notice the `paths` key:
<code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false"> <code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false">
</code-example> </code-example>
@ -150,38 +153,46 @@ Then change the config's `'npm'` path to point to that folder.
The following trivial router sample app shows these changes. The following trivial router sample app shows these changes.
<code-tabs> <code-tabs>
<code-pane title="index.html" path="deployment/src/index.html"> <code-pane title="index.html" path="deployment/src/index.html">
</code-pane> </code-pane>
<code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js"> <code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js">
</code-pane> </code-pane>
<code-pane title="main.ts" path="deployment/src/main.ts"> <code-pane title="main.ts" path="deployment/src/main.ts">
</code-pane> </code-pane>
<code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts"> <code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts"> <code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts"> <code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts">
</code-pane> </code-pane>
<code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts"> <code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts">
</code-pane> </code-pane>
@ -369,6 +380,7 @@ for the missing files. Look at where it _tried_ to find those files and adjust t
Angular apps run in development mode by default, as you can see by the following message on the browser Angular apps run in development mode by default, as you can see by the following message on the browser
console: console:
<code-example format="nocode"> <code-example format="nocode">
Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode. Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
</code-example> </code-example>
@ -378,6 +390,7 @@ Switching to production mode can make it run faster by disabling development spe
To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`. To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`.
<code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false"> <code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false">
</code-example> </code-example>
@ -464,6 +477,7 @@ The list is by no means exhaustive, but should provide you with a good starting
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the - [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
`historyApiFallback` entry in the dev server options as follows: `historyApiFallback` entry in the dev server options as follows:
<code-example> <code-example>
historyApiFallback: { historyApiFallback: {
disableDotRule: true, disableDotRule: true,
@ -478,6 +492,7 @@ The list is by no means exhaustive, but should provide you with a good starting
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) [rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
to the `.htaccess` file as show to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/): [here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
<code-example format="."> <code-example format=".">
RewriteEngine On RewriteEngine On
# If an existing asset or directory is requested go to it as it is # If an existing asset or directory is requested go to it as it is
@ -494,6 +509,7 @@ to the `.htaccess` file as show
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps), [Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
modified to serve `index.html`: modified to serve `index.html`:
<code-example format="."> <code-example format=".">
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
@ -501,6 +517,7 @@ modified to serve `index.html`:
- [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown - [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
[here](http://stackoverflow.com/a/26152011/2116927): [here](http://stackoverflow.com/a/26152011/2116927):
<code-example format="." escape="html"> <code-example format="." escape="html">
<system.webServer> <system.webServer>
<rewrite> <rewrite>
@ -532,6 +549,7 @@ and to
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a - [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites). [rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
<code-example format="."> <code-example format=".">
"rewrites": [ { "rewrites": [ {
"source": "**", "source": "**",

View File

@ -14,6 +14,7 @@ conditionally show a message below the list.
The final UI looks like this: The final UI looks like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"> </img> <img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"> </img>
</figure> </figure>
@ -48,6 +49,7 @@ changing the template and the body of the component.
When you're done, it should look like this: When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts"> <code-example path="displaying-data/src/app/app.component.1.ts">
</code-example> </code-example>
@ -58,6 +60,7 @@ The revised template displays the two component properties using double curly br
interpolation: interpolation:
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
@ -82,6 +85,7 @@ The CSS `selector` in the `@Component` !{_decorator} specifies an element named
That element is a placeholder in the body of your `index.html` file: That element is a placeholder in the body of your `index.html` file:
<code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body"> <code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body">
</code-example> </code-example>
@ -91,6 +95,7 @@ in the `index.html`, finds it, instantiates an instance of `AppComponent`, and r
inside the `<my-app>` tag. inside the `<my-app>` tag.
Now run the app. It should display the title and hero name: Now run the app. It should display the title and hero name:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -114,6 +119,7 @@ In either style, the template data bindings have the same access to the componen
To display a list of heroes, begin by adding !{_an} !{_array} of hero names to the component and redefine `myHero` to be the first name in the !{_array}. To display a list of heroes, begin by adding !{_an} !{_array} of hero names to the component and redefine `myHero` to be the first name in the !{_array}.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example> </code-example>
@ -122,6 +128,7 @@ Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list. each item in the `heroes` list.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
@ -131,6 +138,7 @@ in the `<li>` element is the Angular "repeater" directive.
It marks that `<li>` element (and its children) as the "repeater template": It marks that `<li>` element (and its children) as the "repeater template":
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (li)" region="li"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (li)" region="li">
</code-example> </code-example>
@ -164,6 +172,7 @@ repeat items for any [iterable](guide/!{_iterableUrl}) object.
Now the heroes appear in an unordered list. Now the heroes appear in an unordered list.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -183,6 +192,7 @@ of hero names into !{_an} !{_array} of `Hero` objects. For that you'll need a `H
Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code: Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)"> <code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)">
</code-example> </code-example>
@ -196,6 +206,7 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
Consider the first parameter: Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id"> <code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id">
</code-example> </code-example>
@ -211,6 +222,7 @@ After importing the `Hero` class, the `AppComponent.heroes` property can return
of `Hero` objects: of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes"> <code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes">
</code-example> </code-example>
@ -220,6 +232,7 @@ At the moment it displays the hero's `id` and `name`.
Fix that to display only the hero's `name` property. Fix that to display only the hero's `name` property.
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
@ -236,6 +249,7 @@ The Angular `ngIf` directive inserts or removes an element based on a !{_boolean
To see it in action, add the following paragraph at the bottom of the template: To see it in action, add the following paragraph at the bottom of the template:
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (message)" region="message"> <code-example path="displaying-data/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (message)" region="message">
</code-example> </code-example>
@ -282,23 +296,28 @@ Here's the final code:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final"> <code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
</code-pane> </code-pane>
<code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts"> <code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="displaying-data/src/main.ts"> <code-pane title="main.ts" path="displaying-data/src/main.ts">
</code-pane> </code-pane>

View File

@ -37,6 +37,7 @@ Before components can be added we have to define an anchor point to mark where c
The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template. The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template.
<code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" linenums="false"> <code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" linenums="false">
</code-example> </code-example>
@ -50,28 +51,34 @@ The next step is to implement the ad banner. Most of the implementation is in `A
We start by adding a `template` element with the `AdDirective` directive applied. We start by adding a `template` element with the `AdDirective` directive applied.
<code-tabs> <code-tabs>
<code-pane title="ad-banner.component.ts" path="cb-dynamic-component-loader/src/app/ad-banner.component.ts"> <code-pane title="ad-banner.component.ts" path="cb-dynamic-component-loader/src/app/ad-banner.component.ts">
</code-pane> </code-pane>
<code-pane title="ad.service.ts" path="cb-dynamic-component-loader/src/app/ad.service.ts"> <code-pane title="ad.service.ts" path="cb-dynamic-component-loader/src/app/ad.service.ts">
</code-pane> </code-pane>
<code-pane title="ad-item.ts" path="cb-dynamic-component-loader/src/app/ad-item.ts"> <code-pane title="ad-item.ts" path="cb-dynamic-component-loader/src/app/ad-item.ts">
</code-pane> </code-pane>
<code-pane title="app.module.ts" path="cb-dynamic-component-loader/src/app/app.module.ts"> <code-pane title="app.module.ts" path="cb-dynamic-component-loader/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="app.component" path="cb-dynamic-component-loader/src/app/app.component.ts"> <code-pane title="app.component" path="cb-dynamic-component-loader/src/app/app.component.ts">
</code-pane> </code-pane>
@ -84,6 +91,7 @@ The `template` element decorated with the `ad-host` directive marks where dynami
Using a `template` element is recommended since it doesn't render any additional output. Using a `template` element is recommended since it doesn't render any additional output.
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" linenums="false"> <code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" linenums="false">
</code-example> </code-example>
@ -107,6 +115,7 @@ Generally the compiler will generate a component factory for any component refer
With dynamically loaded components there are no selector references in the templates since components are loaded at runtime. In order to ensure that the compiler will still generate a factory, dynamically loaded components have to be added to their `NgModule`'s `entryComponents` array. With dynamically loaded components there are no selector references in the templates since components are loaded at runtime. In order to ensure that the compiler will still generate a factory, dynamically loaded components have to be added to their `NgModule`'s `entryComponents` array.
<code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" linenums="false"> <code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" linenums="false">
</code-example> </code-example>
@ -120,18 +129,22 @@ In the Ad banner, all components implement a common `AdComponent` interface to s
Two sample components and the `AdComponent` interface are shown below: Two sample components and the `AdComponent` interface are shown below:
<code-tabs> <code-tabs>
<code-pane title="hero-job-ad.component.ts" path="cb-dynamic-component-loader/src/app/hero-job-ad.component.ts"> <code-pane title="hero-job-ad.component.ts" path="cb-dynamic-component-loader/src/app/hero-job-ad.component.ts">
</code-pane> </code-pane>
<code-pane title="hero-profile.component.ts" path="cb-dynamic-component-loader/src/app/hero-profile.component.ts"> <code-pane title="hero-profile.component.ts" path="cb-dynamic-component-loader/src/app/hero-profile.component.ts">
</code-pane> </code-pane>
<code-pane title="ad.component.ts" path="cb-dynamic-component-loader/src/app/ad.component.ts"> <code-pane title="ad.component.ts" path="cb-dynamic-component-loader/src/app/ad.component.ts">
</code-pane> </code-pane>
@ -140,6 +153,7 @@ Two sample components and the `AdComponent` interface are shown below:
</code-tabs> </code-tabs>
The final ad banner looks like this: The final ad banner looks like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img> <img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
</figure> </figure>

View File

@ -43,13 +43,16 @@ Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, s
We bootstrap our `AppModule` in main.ts. We bootstrap our `AppModule` in main.ts.
<code-tabs> <code-tabs>
<code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts"> <code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="cb-dynamic-form/src/main.ts"> <code-pane title="main.ts" path="cb-dynamic-form/src/main.ts">
</code-pane> </code-pane>
@ -67,6 +70,7 @@ The "question" is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class. We have created `QuestionBase` as the most fundamental question class.
<code-example path="cb-dynamic-form/src/app/question-base.ts"> <code-example path="cb-dynamic-form/src/app/question-base.ts">
</code-example> </code-example>
@ -77,6 +81,7 @@ The idea is that the form will be bound to specific question types and render th
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property. `TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property.
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false">
</code-example> </code-example>
@ -84,6 +89,7 @@ The idea is that the form will be bound to specific question types and render th
`DropdownQuestion` presents a list of choices in a select box. `DropdownQuestion` presents a list of choices in a select box.
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false">
</code-example> </code-example>
@ -92,6 +98,7 @@ Next we have defined `QuestionControlService`, a simple service for transforming
In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules. In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules.
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false">
</code-example> </code-example>
@ -100,13 +107,16 @@ In a nutshell, the form group consumes the metadata from the question model and
Now that we have defined the complete model we are ready to create components to represent the dynamic form. Now that we have defined the complete model we are ready to create components to represent the dynamic form.
`DynamicFormComponent` is the entry point and the main container for the form. `DynamicFormComponent` is the entry point and the main container for the form.
<code-tabs> <code-tabs>
<code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html"> <code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html">
</code-pane> </code-pane>
<code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts"> <code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts">
</code-pane> </code-pane>
@ -119,13 +129,16 @@ The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object. the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object.
<code-tabs> <code-tabs>
<code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html"> <code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html">
</code-pane> </code-pane>
<code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts"> <code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts">
</code-pane> </code-pane>
@ -150,6 +163,7 @@ underlying control objects, populated from the question model with display and v
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array. Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array.
<code-example path="cb-dynamic-form/src/app/question.service.ts"> <code-example path="cb-dynamic-form/src/app/question.service.ts">
</code-example> </code-example>
@ -157,6 +171,7 @@ underlying control objects, populated from the question model with display and v
Finally, we display an instance of the form in the `AppComponent` shell. Finally, we display an instance of the form in the `AppComponent` shell.
<code-example path="cb-dynamic-form/src/app/app.component.ts"> <code-example path="cb-dynamic-form/src/app/app.component.ts">
</code-example> </code-example>
@ -176,6 +191,7 @@ When the form is valid, we can click *Save* and the app renders the current form
This proves that any user input is bound back to the data model. This proves that any user input is bound back to the data model.
Saving and retrieving the data is an exercise for another time. Saving and retrieving the data is an exercise for another time.
The final form looks like this: The final form looks like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>

View File

@ -44,6 +44,7 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a live-example} {@a live-example}
**Try the live example to see and download the full cookbook source code.** **Try the live example to see and download the full cookbook source code.**
<live-example name="cb-form-validation" embedded=true img="cookbooks/form-validation/plunker.png"> <live-example name="cb-form-validation" embedded=true img="cookbooks/form-validation/plunker.png">
</live-example> </live-example>
@ -71,6 +72,7 @@ In this first template validation example,
notice the HTML that reads the control state and updates the display appropriately. notice the HTML that reads the control state and updates the display appropriately.
Here's an excerpt from the template HTML for a single input control bound to the hero name: Here's an excerpt from the template HTML for a single input control bound to the hero name:
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" linenums="false">
</code-example> </code-example>
@ -114,6 +116,7 @@ The component class manages the hero model used in the data binding
as well as other code to support the view. as well as other code to support the view.
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class"> <code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class">
</code-example> </code-example>
@ -123,13 +126,16 @@ Use this template-driven validation technique when working with static forms wit
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach: Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach:
<code-tabs> <code-tabs>
<code-pane title="template/hero-form-template1.component.html" path="cb-form-validation/src/app/template/hero-form-template1.component.html"> <code-pane title="template/hero-form-template1.component.html" path="cb-form-validation/src/app/template/hero-form-template1.component.html">
</code-pane> </code-pane>
<code-pane title="template/hero-form-template1.component.ts" path="cb-form-validation/src/app/template/hero-form-template1.component.ts"> <code-pane title="template/hero-form-template1.component.ts" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane> </code-pane>
@ -160,13 +166,16 @@ the template and component.
Here's the hero name again, excerpted from the revised template Here's the hero name again, excerpted from the revised template
(Template 2), next to the original version: (Template 2), next to the original version:
<code-tabs> <code-tabs>
<code-pane title="hero-form-template2.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg"> <code-pane title="hero-form-template2.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg">
</code-pane> </code-pane>
<code-pane title="hero-form-template1.component.html (name #1)" path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg"> <code-pane title="hero-form-template1.component.html (name #1)" path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg">
</code-pane> </code-pane>
@ -199,6 +208,7 @@ The first step is to acquire the form control that Angular created from the temp
Look back at the top of the component template at the Look back at the top of the component template at the
`#heroForm` template variable in the `<form>` element: `#heroForm` template variable in the `<form>` element:
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="form-tag" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="form-tag" linenums="false">
</code-example> </code-example>
@ -206,6 +216,7 @@ Look back at the top of the component template at the
The `heroForm` variable is a reference to the control model that Angular derived from the template. The `heroForm` variable is a reference to the control model that Angular derived from the template.
Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query: Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child" linenums="false">
</code-example> </code-example>
@ -225,6 +236,7 @@ That's the right time to see if there's a new `heroForm` object.
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property. - When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
The `onValueChanged` handler looks for validation errors after every keystroke. The `onValueChanged` handler looks for validation errors after every keystroke.
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="handler" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="handler" linenums="false">
</code-example> </code-example>
@ -246,6 +258,7 @@ For each field, the `onValueChanged` handler does the following:
Next, the component needs some error messages of course&mdash;a set for each validated property with Next, the component needs some error messages of course&mdash;a set for each validated property with
one message per validation rule: one message per validation rule:
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="messages" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="messages" linenums="false">
</code-example> </code-example>
@ -290,6 +303,7 @@ You've been reviewing the "Template-driven" approach which requires the `FormsMo
Here's how you imported it in the `HeroFormTemplateModule`. Here's how you imported it in the `HeroFormTemplateModule`.
<code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" linenums="false">
</code-example> </code-example>
@ -336,6 +350,7 @@ The following cookbook sample re-writes the hero form in _reactive forms_ style.
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`. The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
The application module for the reactive forms feature in this sample looks like this: The application module for the reactive forms feature in this sample looks like this:
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.module.ts" linenums="false"> <code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.module.ts" linenums="false">
</code-example> </code-example>
@ -352,6 +367,7 @@ to the `heroForm` property in the component class.
The `heroForm` is the control model that the component class builds and maintains. The `heroForm` is the control model that the component class builds and maintains.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" linenums="false"> <code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" linenums="false">
</code-example> </code-example>
@ -359,13 +375,16 @@ The `heroForm` is the control model that the component class builds and maintain
Next, modify the template HTML elements to match the _reactive forms_ style. Next, modify the template HTML elements to match the _reactive forms_ style.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version: Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
<code-tabs> <code-tabs>
<code-pane title="hero-form-reactive.component.html (name #3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg"> <code-pane title="hero-form-reactive.component.html (name #3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg">
</code-pane> </code-pane>
<code-pane title="hero-form-template1.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg"> <code-pane title="hero-form-template1.component.html (name #2)" path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-with-error-msg">
</code-pane> </code-pane>
@ -419,13 +438,16 @@ the help of the `FormBuilder` class.
Here's the section of code devoted to that process, paired with the template-driven code it replaces: Here's the section of code devoted to that process, paired with the template-driven code it replaces:
<code-tabs> <code-tabs>
<code-pane title="reactive/hero-form-reactive.component.ts (FormBuilder)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="form-builder"> <code-pane title="reactive/hero-form-reactive.component.ts (FormBuilder)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="form-builder">
</code-pane> </code-pane>
<code-pane title="template/hero-form-template2.component.ts (ViewChild)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child"> <code-pane title="template/hero-form-template2.component.ts (ViewChild)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child">
</code-pane> </code-pane>
@ -487,6 +509,7 @@ This sample updates the model twice:
The `onSubmit()` method simply replaces the `hero` object with the combined values of the form: The `onSubmit()` method simply replaces the `hero` object with the combined values of the form:
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" linenums="false"> <code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" linenums="false">
</code-example> </code-example>
@ -502,6 +525,7 @@ correspond _exactly_ to the hero data object properties.
The `addHero()` method discards pending changes and creates a brand new `hero` model object. The `addHero()` method discards pending changes and creates a brand new `hero` model object.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" linenums="false"> <code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" linenums="false">
</code-example> </code-example>
@ -511,18 +535,22 @@ The `<form>` tag's `[formGroup]` binding refreshes the page with the new control
Here's the complete reactive component file, compared to the two template-driven component files. Here's the complete reactive component file, compared to the two template-driven component files.
<code-tabs> <code-tabs>
<code-pane title="reactive/hero-form-reactive.component.ts (#3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts"> <code-pane title="reactive/hero-form-reactive.component.ts (#3)" path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts">
</code-pane> </code-pane>
<code-pane title="template/hero-form-template2.component.ts (#2)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts"> <code-pane title="template/hero-form-template2.component.ts (#2)" path="cb-form-validation/src/app/template/hero-form-template2.component.ts">
</code-pane> </code-pane>
<code-pane title="template/hero-form-template1.component.ts (#1)" path="cb-form-validation/src/app/template/hero-form-template1.component.ts"> <code-pane title="template/hero-form-template1.component.ts (#1)" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane> </code-pane>
@ -551,6 +579,7 @@ and declared in the `SharedModule`.
Here's the `forbiddenNamevalidator()` function: Here's the `forbiddenNamevalidator()` function:
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" linenums="false"> <code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" linenums="false">
</code-example> </code-example>
@ -574,6 +603,7 @@ and whose value is an arbitrary dictionary of values that you could insert into
In the reactive forms component, the `'name'` control's validator function list In the reactive forms component, the `'name'` control's validator function list
has a `forbiddenNameValidator` at the bottom. has a `forbiddenNameValidator` at the bottom.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" linenums="false"> <code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" linenums="false">
</code-example> </code-example>
@ -581,6 +611,7 @@ has a `forbiddenNameValidator` at the bottom.
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`) In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
of a custom _attribute directive_, which rejects "bob". of a custom _attribute directive_, which rejects "bob".
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-input" linenums="false"> <code-example path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-input" linenums="false">
</code-example> </code-example>
@ -590,12 +621,14 @@ The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbidd
Angular `forms` recognizes the directive's role in the validation process because the directive registers itself Angular `forms` recognizes the directive's role in the validation process because the directive registers itself
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives. with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives.
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" linenums="false"> <code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" linenums="false">
</code-example> </code-example>
Here is the rest of the directive to help you get an idea of how it all comes together: Here is the rest of the directive to help you get an idea of how it all comes together:
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive"> <code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive">
</code-example> </code-example>

View File

@ -50,6 +50,7 @@ otherwise wrestle with yourself.
You'll 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'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -61,6 +62,7 @@ Two of the three fields on this form are required. Required fields have a green
If you 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'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -103,6 +105,7 @@ and one optional field (`alterEgo`).
In the `!{_appDir}` directory, create the following file with the given content: In the `!{_appDir}` directory, create the following file with the given content:
<code-example path="forms/src/app/hero.ts"> <code-example path="forms/src/app/hero.ts">
</code-example> </code-example>
@ -117,6 +120,7 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
You can create a new hero like this: You can create a new hero like this:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
</code-example> </code-example>
@ -131,6 +135,7 @@ Begin with the class because it states, in brief, what the hero editor can do.
Create the following file with the given content: Create the following file with the given content:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1">
</code-example> </code-example>
@ -175,6 +180,7 @@ Because template-driven forms are in their own module, you need to add the `Form
Replace the contents of the "QuickStart" version with the following: Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.module.ts"> <code-example path="forms/src/app/app.module.ts">
</code-example> </code-example>
@ -214,6 +220,7 @@ If you wrote it and it should belong to this module, _do_ declare it in th
Replace the contents of the "QuickStart" version with the following: Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.component.ts"> <code-example path="forms/src/app/app.component.ts">
</code-example> </code-example>
@ -236,6 +243,7 @@ You've also dropped the `name` field from the class body.
Create the template file with the following contents: Create the template file with the following contents:
<code-example path="forms/src/app/hero-form.component.html" region="start"> <code-example path="forms/src/app/hero-form.component.html" region="start">
</code-example> </code-example>
@ -258,6 +266,7 @@ Bootstrap gives the form a little style.
~~~ {.callout.is-important} ~~~ {.callout.is-important}
<header> <header>
Angular forms don't require a style library Angular forms don't require a style library
</header> </header>
@ -271,6 +280,7 @@ the styles of any external library. Angular apps can use any CSS library or none
To add the stylesheet, open `index.html` and add the following link to the `<head>`: To add the stylesheet, open `index.html` and add the following link to the `<head>`:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap"> <code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
</code-example> </code-example>
@ -288,6 +298,7 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
Add the following HTML *immediately below* the *Alter Ego* group: Add the following HTML *immediately below* the *Alter Ego* group:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers">
</code-example> </code-example>
@ -300,6 +311,7 @@ you display its name using the interpolation syntax.
Running the app right now would be disappointing. Running the app right now would be disappointing.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -319,6 +331,7 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this: Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example> </code-example>
@ -341,6 +354,7 @@ adding and deleting characters, you'd see them appear and disappear
from the interpolated text. from the interpolated text.
At some point it might look like this: At some point it might look like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -382,6 +396,7 @@ Then you can confirm that two-way data binding works *for the entire hero model*
After revision, the core of the form should look like this: After revision, the core of the form should look like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example> </code-example>
@ -399,6 +414,7 @@ to match the label to its input control.
If you run the app now and change 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'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -416,20 +432,25 @@ you if the user touched the control, if the value changed, or if the value becam
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state. The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
You 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> <table>
<tr> <tr>
<th> <th>
State State
</th> </th>
<th> <th>
Class if true Class if true
</th> </th>
<th> <th>
Class if false Class if false
</th> </th>
@ -438,18 +459,22 @@ You can leverage those class names to change the appearance of the control.
</tr> </tr>
<tr> <tr>
<td> <td>
The control has been visited. The control has been visited.
</td> </td>
<td> <td>
<code>ng-touched</code> <code>ng-touched</code>
</td> </td>
<td> <td>
<code>ng-untouched</code> <code>ng-untouched</code>
</td> </td>
@ -458,18 +483,22 @@ You can leverage those class names to change the appearance of the control.
</tr> </tr>
<tr> <tr>
<td> <td>
The control's value has changed. The control's value has changed.
</td> </td>
<td> <td>
<code>ng-dirty</code> <code>ng-dirty</code>
</td> </td>
<td> <td>
<code>ng-pristine</code> <code>ng-pristine</code>
</td> </td>
@ -478,18 +507,22 @@ You can leverage those class names to change the appearance of the control.
</tr> </tr>
<tr> <tr>
<td> <td>
The control's value is valid. The control's value is valid.
</td> </td>
<td> <td>
<code>ng-valid</code> <code>ng-valid</code>
</td> </td>
<td> <td>
<code>ng-invalid</code> <code>ng-invalid</code>
</td> </td>
@ -504,6 +537,7 @@ Temporarily add a [template reference variable](guide/template-syntax) named `sp
to the _Name_ `<input>` tag and use it to display the input's CSS classes. to the _Name_ `<input>` tag and use it to display the input's CSS classes.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2">
</code-example> </code-example>
@ -518,12 +552,14 @@ Follow these steps *precisely*:
The actions and effects are as follows: The actions and effects are as follows:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
You should see the following transitions and class names: You should see the following transitions and class names:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -539,6 +575,7 @@ To create such visual feedback, add definitions for the `ng-*` CSS classes.
You 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: on the left of the input box:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -547,6 +584,7 @@ You achieve this effect by adding these class definitions to a new `forms.css` f
that you add to the project as a sibling to `index.html`: that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/forms.css"> <code-example path="forms/src/forms.css">
</code-example> </code-example>
@ -554,6 +592,7 @@ that you add to the project as a sibling to `index.html`:
Update the `<head>` of `index.html` to include this style sheet: Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles"> <code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
</code-example> </code-example>
@ -566,6 +605,7 @@ Leverage the control's state to reveal a helpful message.
When the user deletes the name, the form should look like this: When the user deletes the name, the form should look like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -577,6 +617,7 @@ To achieve this effect, extend the `<input>` tag with the following:
Here's an example of an error message added to the _name_ input box: Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg">
</code-example> </code-example>
@ -599,6 +640,7 @@ You control visibility of the name error message by binding properties of the `n
control to the message `<div>` element's `hidden` property. control to the message `<div>` element's `hidden` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
</code-example> </code-example>
@ -626,12 +668,14 @@ 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. Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset"> <code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset">
</code-example> </code-example>
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false"> <code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false">
</code-example> </code-example>
@ -654,6 +698,7 @@ 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. by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset"> <code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset">
</code-example> </code-example>
@ -672,6 +717,7 @@ To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method: to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example> </code-example>
@ -705,6 +751,7 @@ the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code: using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button">
</code-example> </code-example>
@ -747,6 +794,7 @@ Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property. its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div">
</code-example> </code-example>
@ -756,6 +804,7 @@ The main form is visible from the start because the
as this fragment from the `HeroFormComponent` shows: as this fragment from the `HeroFormComponent` shows:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted">
</code-example> </code-example>
@ -767,6 +816,7 @@ Now the app needs to show something else while the form is in the submitted stat
Add the following HTML below the `<div>` wrapper you just wrote: Add the following HTML below the `<div>` wrapper you just wrote:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted">
</code-example> </code-example>
@ -796,34 +846,43 @@ framework features to provide support for data modification, validation, and mor
The final project folder structure should look like this: The final project folder structure should look like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-forms angular-forms
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-form.component.html hero-form.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-form.component.ts hero-form.component.ts
</aio-file> </aio-file>
@ -832,16 +891,19 @@ The final project folder structure should look like this:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
@ -850,11 +912,13 @@ The final project folder structure should look like this:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -868,43 +932,52 @@ The final project folder structure should look like this:
Heres the code for the final version of the application: Heres the code for the final version of the application:
<code-tabs> <code-tabs>
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final"> <code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
</code-pane> </code-pane>
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final"> <code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
</code-pane> </code-pane>
<code-pane title="hero.ts" path="forms/src/app/hero.ts"> <code-pane title="hero.ts" path="forms/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="app.module.ts" path="forms/src/app/app.module.ts"> <code-pane title="app.module.ts" path="forms/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts"> <code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="forms/src/main.ts"> <code-pane title="main.ts" path="forms/src/main.ts">
</code-pane> </code-pane>
<code-pane title="index.html" path="forms/src/index.html"> <code-pane title="index.html" path="forms/src/index.html">
</code-pane> </code-pane>
<code-pane title="forms.css" path="forms/src/forms.css"> <code-pane title="forms.css" path="forms/src/forms.css">
</code-pane> </code-pane>

View File

@ -45,6 +45,7 @@ The `HeroesListComponent` holds and manages multiple instances of the `HeroTaxRe
The following diagram represents the state of the this guide's three-level component tree when there are three instances of `HeroTaxReturnComponent` The following diagram represents the state of the this guide's three-level component tree when there are three instances of `HeroTaxReturnComponent`
open simultaneously. open simultaneously.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -100,6 +101,7 @@ That's not supposed to happen but providing the service in the root `AppModule`
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this: Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata"> <code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata">
</code-example> </code-example>
@ -128,6 +130,7 @@ Each tax return component has the following characteristics:
* Can change a tax return without affecting a return in another component. * Can change a tax return without affecting a return in another component.
* Has the ability to save the changes to its tax return or cancel them. * Has the ability to save the changes to its tax return or cancel them.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -142,6 +145,7 @@ It caches a single `HeroTaxReturn`, tracks changes to that return, and can save
It also delegates to the application-wide singleton `HeroService`, which it gets by injection. It also delegates to the application-wide singleton `HeroService`, which it gets by injection.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts">
</code-example> </code-example>
@ -149,6 +153,7 @@ It also delegates to the application-wide singleton `HeroService`, which it gets
Here is the `HeroTaxReturnComponent` that makes use of it. Here is the `HeroTaxReturnComponent` that makes use of it.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts">
</code-example> </code-example>
@ -166,6 +171,7 @@ What a mess!
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property. Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example> </code-example>
@ -200,6 +206,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`. Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -210,6 +217,7 @@ When you resolve an instance of `Car` at the deepest component (C),
its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and
`Tires` resolved by the root injector (A). `Tires` resolved by the root injector (A).
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>

View File

@ -85,6 +85,7 @@ After translation, the compiler removes it.
In the accompanying sample, an `<h1>` tag displays a simple English language greeting In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish: that you translate into Spanish:
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false">
</code-example> </code-example>
@ -92,6 +93,7 @@ that you translate into Spanish:
Add the `i18n` attribute to the tag to mark it for translation. Add the `i18n` attribute to the tag to mark it for translation.
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false">
</code-example> </code-example>
@ -103,6 +105,7 @@ need a description of the message.
Assign a description to the i18n attribute: Assign a description to the i18n attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false">
</code-example> </code-example>
@ -114,6 +117,7 @@ In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`): separating it from the description with the `|` character (`<meaning>|<description>`):
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false">
</code-example> </code-example>
@ -134,6 +138,7 @@ Here are two techniques to try.
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered: (1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false">
</code-example> </code-example>
@ -141,6 +146,7 @@ Here are two techniques to try.
(2) Wrap the text in a pair of HTML comments: (2) Wrap the text in a pair of HTML comments:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false">
</code-example> </code-example>
@ -153,6 +159,7 @@ Here are two techniques to try.
You've added an image to your template. You care about accessibility too so you add a `title` attribute: You've added an image to your template. You care about accessibility too so you add a `title` attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false">
</code-example> </code-example>
@ -164,6 +171,7 @@ name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write: To translate the `title` on the `img` tag from the previous example, write:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false">
</code-example> </code-example>
@ -184,6 +192,7 @@ Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves: Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false">
</code-example> </code-example>
@ -240,6 +249,7 @@ property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation: The message maps those values to the appropriate translation:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example> </code-example>
@ -256,6 +266,7 @@ into a translation source file in an industry standard format.
This is an Angular CLI tool in the `@angular/compiler-cli` npm package. This is an Angular CLI tool in the `@angular/compiler-cli` npm package.
If you haven't already installed the CLI and its `platform-server` peer dependency, do so now: If you haven't already installed the CLI and its `platform-server` peer dependency, do so now:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm install @angular/compiler-cli @angular/platform-server --save npm install @angular/compiler-cli @angular/platform-server --save
@ -263,6 +274,7 @@ If you haven't already installed the CLI and its `platform-server` peer dependen
Open a terminal window at the root of the application project and enter the `ng-xi18n` command: Open a terminal window at the root of the application project and enter the `ng-xi18n` command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n ./node_modules/.bin/ng-xi18n
@ -288,6 +300,7 @@ You can generate a file named **`messages.xmb`** in the
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" target="_blank">XML Message Bundle (XMB)</a> format <a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" target="_blank">XML Message Bundle (XMB)</a> format
by adding the `--i18nFormat=xmb` flag. by adding the `--i18nFormat=xmb` flag.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n --i18nFormat=xmb ./node_modules/.bin/ng-xi18n --i18nFormat=xmb
@ -302,6 +315,7 @@ You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration For example, if the `tsconfig.json` TypeScript configuration
file is located somewhere other than in the root folder, file is located somewhere other than in the root folder,
you must identify the path to it with the `-p` option: you must identify the path to it with the `-p` option:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json ./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json ./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
@ -316,6 +330,7 @@ you must identify the path to it with the `-p` option:
Consider adding a convenience shortcut to the `scripts` section of the `package.json` Consider adding a convenience shortcut to the `scripts` section of the `package.json`
to make the command easier to remember and run: to make the command easier to remember and run:
<code-example format='.' language='sh'> <code-example format='.' language='sh'>
"scripts": { "scripts": {
"i18n": "ng-xi18n", "i18n": "ng-xi18n",
@ -324,6 +339,7 @@ to make the command easier to remember and run:
</code-example> </code-example>
Now you can issue command variations such as these: Now you can issue command variations such as these:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm run i18n npm run i18n
npm run i18n -- -p path/to/tsconfig.json npm run i18n -- -p path/to/tsconfig.json
@ -378,6 +394,7 @@ This sample file is easy to translate without a special editor or knowledge of S
Open `messages.es.xlf` and find the first `<trans-unit>` section: Open `messages.es.xlf` and find the first `<trans-unit>` section:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example> </code-example>
@ -387,6 +404,7 @@ This XML element represents the translation of the `<h1>` greeting tag you marke
Using the _source_, _description_, and _meaning_ elements to guide your translation, Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting: replace the `<target/>` tag with the Spanish greeting:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example> </code-example>
@ -406,6 +424,7 @@ See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
Translate the other text nodes the same way: Translate the other text nodes the same way:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false">
</code-example> </code-example>
@ -427,6 +446,7 @@ In this example, you know the translation unit for the `select` must be just bel
To translate a `plural`, translate its ICU format match values: To translate a `plural`, translate its ICU format match values:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false">
</code-example> </code-example>
@ -435,6 +455,7 @@ To translate a `plural`, translate its ICU format match values:
The `select` behaves a little differently. Here again is the ICU format message in the component template: The `select` behaves a little differently. Here again is the ICU format message in the component template:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example> </code-example>
@ -446,6 +467,7 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and leave the placeholder where it is. Translate the text and leave the placeholder where it is.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false">
</code-example> </code-example>
@ -453,6 +475,7 @@ Translate the text and leave the placeholder where it is.
The second translation unit, immediately below the first one, contains the `select` message. Translate that. The second translation unit, immediately below the first one, contains the `select` message. Translate that.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false">
</code-example> </code-example>
@ -460,11 +483,13 @@ The second translation unit, immediately below the first one, contains the `sele
Here they are together, after translation: Here they are together, after translation:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false">
</code-example> </code-example>
<div class='l-main-content'> <div class='l-main-content'>
</div> </div>
@ -472,6 +497,7 @@ Here they are together, after translation:
The entire template translation is complete. It's The entire template translation is complete. It's
time to incorporate that translation into the application. time to incorporate that translation into the application.
<div id='app-pre-translation'> <div id='app-pre-translation'>
</div> </div>
@ -481,28 +507,34 @@ time to incorporate that translation into the application.
When the previous steps finish, the sample app _and_ its translation file are as follows: When the previous steps finish, the sample app _and_ its translation file are as follows:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts"> <code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts">
</code-pane> </code-pane>
<code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html"> <code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html">
</code-pane> </code-pane>
@ -548,6 +580,7 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows: Open `index.html` and revise the launch script as follows:
<code-example path="cb-i18n/src/index.html" region="i18n" linenums="false"> <code-example path="cb-i18n/src/index.html" region="i18n" linenums="false">
</code-example> </code-example>
@ -567,6 +600,7 @@ You'll need it to import the language translation file.
SystemJS doesn't ship with a raw text plugin but it's easy to add. SystemJS doesn't ship with a raw text plugin but it's easy to add.
Create the following `systemjs-text-plugin.js` in the `src/` folder: Create the following `systemjs-text-plugin.js` in the `src/` folder:
<code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false"> <code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false">
</code-example> </code-example>
@ -584,6 +618,7 @@ The `getTranslationProviders` function in the following `src/app/i18n-providers.
creates those providers based on the user's _locale_ creates those providers based on the user's _locale_
and the corresponding translation file: and the corresponding translation file:
<code-example path="cb-i18n/src/app/i18n-providers.ts"> <code-example path="cb-i18n/src/app/i18n-providers.ts">
</code-example> </code-example>
@ -613,6 +648,7 @@ You'll create an _options_ object with the translation providers from `getTransl
and pass it to `bootstrapModule`. and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows: Open the `src/main.ts` and modify the bootstrap code as follows:
<code-example path="cb-i18n/src/main.ts" linenums="false"> <code-example path="cb-i18n/src/main.ts" linenums="false">
</code-example> </code-example>
@ -659,6 +695,7 @@ Tell AOT how to translate by adding three options to the `ngc` command:
* `--i18nFormat`: the format of the localization file * `--i18nFormat`: the format of the localization file
For this sample, the Spanish language command would be For this sample, the Spanish language command would be
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf ./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
@ -669,6 +706,7 @@ For this sample, the Spanish language command would be
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Windows users may have to quote the command: Windows users may have to quote the command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf "./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf

View File

@ -15,25 +15,31 @@ The documentation is divided into major thematic sections, each
a collection of pages devoted to that theme. a collection of pages devoted to that theme.
<table width="100%"> <table width="100%">
<col width="15%"> <col width="15%">
</col> </col>
<col> <col>
</col> </col>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../quickstart.html">QuickStart</a></b> <b><a href="../quickstart.html">QuickStart</a></b>
</td> </td>
<td> <td>
A first taste of Angular<span if-docs="ts"> with zero installation. A first taste of Angular<span if-docs="ts"> with zero installation.
Run "Hello World" in an online code editor and start playing with live code</span>. Run "Hello World" in an online code editor and start playing with live code</span>.
@ -43,13 +49,16 @@ a collection of pages devoted to that theme.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b>Guide</b> <b>Guide</b>
</td> </td>
<td> <td>
Learn the Angular basics (you're already here!) like the setup for local development, Learn the Angular basics (you're already here!) like the setup for local development,
displaying data and accepting user input, injecting application services into components, displaying data and accepting user input, injecting application services into components,
@ -60,13 +69,16 @@ a collection of pages devoted to that theme.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../api/">API Reference</a></b> <b><a href="../api/">API Reference</a></b>
</td> </td>
<td> <td>
Authoritative details about each of the Angular libraries. Authoritative details about each of the Angular libraries.
</td> </td>
@ -75,13 +87,16 @@ a collection of pages devoted to that theme.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../tutorial/">Tutorial</a></b> <b><a href="../tutorial/">Tutorial</a></b>
</td> </td>
<td> <td>
A step-by-step, immersive approach to learning Angular that A step-by-step, immersive approach to learning Angular that
introduces the major features of Angular in an application context. introduces the major features of Angular in an application context.
@ -91,13 +106,16 @@ a collection of pages devoted to that theme.
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href=" ">Advanced</a></b> <b><a href=" ">Advanced</a></b>
</td> </td>
<td> <td>
In-depth analysis of Angular features and development practices. In-depth analysis of Angular features and development practices.
</td> </td>
@ -106,13 +124,16 @@ a collection of pages devoted to that theme.
</tr> </tr>
<tr style=top if-docs="ts"> <tr style=top if-docs="ts">
<td> <td>
<b><a href="../cookbook/">Cookbook</a></b> <b><a href="../cookbook/">Cookbook</a></b>
</td> </td>
<td> <td>
Recipes for specific application challenges, mostly code snippets with a minimum of exposition. Recipes for specific application challenges, mostly code snippets with a minimum of exposition.

View File

@ -7,6 +7,7 @@ A suggested path through the documentation for Angular newcomers.
@description @description
<figure> <figure>
<img src="assets/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"> </img> <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> </figure>

View File

@ -7,6 +7,7 @@ Angular calls lifecycle hook methods on directives and components as it creates,
@description @description
<figure> <figure>
<img src="assets/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"> </img> <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> </figure>
@ -35,6 +36,7 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit()` For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component: that Angular calls shortly after creating the component:
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false"> <code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false">
</code-example> </code-example>
@ -48,25 +50,31 @@ Angular only calls a directive/component hook method *if it is defined*.
## Lifecycle sequence ## Lifecycle sequence
*After* creating a component/directive by calling its constructor, Angular *After* creating a component/directive by calling its constructor, Angular
calls the lifecycle hook methods in the following sequence at specific moments: calls the lifecycle hook methods in the following sequence at specific moments:
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
Hook Hook
</th> </th>
<th> <th>
Purpose and Timing Purpose and Timing
</th> </th>
@ -75,13 +83,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngOnChanges()</code> <code>ngOnChanges()</code>
</td> </td>
<td> <td>
Respond when Angular (re)sets data-bound input properties. Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values. The method receives a `SimpleChanges` object of current and previous property values.
@ -94,13 +105,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngOnInit()</code> <code>ngOnInit()</code>
</td> </td>
<td> <td>
Initialize the directive/component after Angular first displays the data-bound properties Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties. and sets the directive/component's input properties.
@ -113,13 +127,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngDoCheck()</code> <code>ngDoCheck()</code>
</td> </td>
<td> <td>
Detect and act upon changes that Angular can't or won't detect on its own. Detect and act upon changes that Angular can't or won't detect on its own.
@ -131,13 +148,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngAfterContentInit()</code> <code>ngAfterContentInit()</code>
</td> </td>
<td> <td>
Respond after Angular projects external content into the component's view. Respond after Angular projects external content into the component's view.
@ -151,13 +171,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngAfterContentChecked()</code> <code>ngAfterContentChecked()</code>
</td> </td>
<td> <td>
Respond after Angular checks the content projected into the component. Respond after Angular checks the content projected into the component.
@ -171,13 +194,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngAfterViewInit()</code> <code>ngAfterViewInit()</code>
</td> </td>
<td> <td>
Respond after Angular initializes the component's views and child views. Respond after Angular initializes the component's views and child views.
@ -191,13 +217,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngAfterViewChecked()</code> <code>ngAfterViewChecked()</code>
</td> </td>
<td> <td>
Respond after Angular checks the component's views and child views. Respond after Angular checks the component's views and child views.
@ -211,13 +240,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<code>ngOnDestroy</code> <code>ngOnDestroy</code>
</td> </td>
<td> <td>
Cleanup just before Angular destroys the directive/component. Cleanup just before Angular destroys the directive/component.
Unsubscribe Observables and detach event handlers to avoid memory leaks. Unsubscribe Observables and detach event handlers to avoid memory leaks.
@ -254,25 +286,31 @@ a *child* component that illustrates one or more of the lifecycle hook methods.
Here's a brief description of each exercise: Here's a brief description of each exercise:
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
Component Component
</th> </th>
<th> <th>
Description Description
</th> </th>
@ -281,13 +319,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#peek-a-boo">Peek-a-boo</a> <a href="#peek-a-boo">Peek-a-boo</a>
</td> </td>
<td> <td>
Demonstrates every lifecycle hook. Demonstrates every lifecycle hook.
Each hook method writes to the on-screen log. Each hook method writes to the on-screen log.
@ -297,13 +338,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#spy">Spy</a> <a href="#spy">Spy</a>
</td> </td>
<td> <td>
Directives have lifecycle hooks too. Directives have lifecycle hooks too.
A `SpyDirective` can log when the element it spies upon is A `SpyDirective` can log when the element it spies upon is
@ -317,13 +361,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#onchanges">OnChanges</a> <a href="#onchanges">OnChanges</a>
</td> </td>
<td> <td>
See how Angular calls the `ngOnChanges()` hook with a `changes` object See how Angular calls the `ngOnChanges()` hook with a `changes` object
every time one of the component input properties changes. every time one of the component input properties changes.
@ -334,13 +381,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#docheck">DoCheck</a> <a href="#docheck">DoCheck</a>
</td> </td>
<td> <td>
Implements an `ngDoCheck()` method with custom change detection. Implements an `ngDoCheck()` method with custom change detection.
See how often Angular calls this hook and watch it post changes to a log. See how often Angular calls this hook and watch it post changes to a log.
@ -350,13 +400,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#afterview">AfterView</a> <a href="#afterview">AfterView</a>
</td> </td>
<td> <td>
Shows what Angular means by a *view*. Shows what Angular means by a *view*.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks. Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
@ -366,13 +419,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<a href="#aftercontent">AfterContent</a> <a href="#aftercontent">AfterContent</a>
</td> </td>
<td> <td>
Shows how to project external content into a component and Shows how to project external content into a component and
how to distinguish projected content from a component's view children. how to distinguish projected content from a component's view children.
@ -383,13 +439,16 @@ Here's a brief description of each exercise:
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
Counter Counter
</td> </td>
<td> <td>
Demonstrates a combination of a component and a directive Demonstrates a combination of a component and a directive
each with its own hooks. each with its own hooks.
@ -419,6 +478,7 @@ You would rarely, if ever, implement all of the interfaces like this.
The peek-a-boo exists to show how Angular calls the hooks in the expected order. 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. This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -471,6 +531,7 @@ The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` a
that log messages to the parent via an injected `LoggerService`. that log messages to the parent via an injected `LoggerService`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false"> <code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false">
</code-example> </code-example>
@ -479,6 +540,7 @@ You can apply the spy to any native or component element and it'll be initialize
at the same time as that element. at the same time as that element.
Here it is attached to the repeated hero `<div>`: Here it is attached to the repeated hero `<div>`:
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false">
</code-example> </code-example>
@ -486,6 +548,7 @@ Here it is attached to the repeated hero `<div>`:
Each spy's birth and death marks the birth and death of the attached hero `<div>` 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: with an entry in the *Hook Log* as seen here:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -562,6 +625,7 @@ You risk memory leaks if you neglect to do so.
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive). Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook. This example monitors the `OnChanges` hook.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false"> <code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false">
</code-example> </code-example>
@ -572,6 +636,7 @@ This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`. The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false"> <code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false">
</code-example> </code-example>
@ -579,12 +644,14 @@ The example component, `OnChangesComponent`, has two input properties: `hero` an
The host `OnChangesParentComponent` binds to them like this: The host `OnChangesParentComponent` binds to them like this:
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes"> <code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes">
</code-example> </code-example>
Here's the sample in action as the user makes changes. Here's the sample in action as the user makes changes.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -612,6 +679,7 @@ Use this method to detect a change that Angular overlooked.
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook: The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false"> <code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false">
</code-example> </code-example>
@ -620,6 +688,7 @@ This code inspects certain _values of interest_, capturing and comparing their c
It writes a special message to the log when there are no substantive changes to the `hero` or the `power` It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
so you can see how often `DoCheck` is called. The results are illuminating: so you can see how often `DoCheck` is called. The results are illuminating:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -643,12 +712,14 @@ The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` h
Here's a child view that displays a hero's name in an `<input>`: Here's a child view that displays a hero's name in an `<input>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false">
</code-example> </code-example>
The `AfterViewComponent` displays this child view *within its template*: The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -658,6 +729,7 @@ which can only be reached by querying for the child view via the property decora
[@ViewChild](api/core/index/ViewChild-decorator). [@ViewChild](api/core/index/ViewChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false">
</code-example> </code-example>
@ -669,6 +741,7 @@ which can only be reached by querying for the child view via the property decora
The `doSomething()` method updates the screen when the hero name exceeds 10 characters. The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false">
</code-example> </code-example>
@ -682,6 +755,7 @@ Angular throws an error if the hook updates the component's data-bound `comment`
The `LoggerService.tick_then()` postpones the log update The `LoggerService.tick_then()` postpones the log update
for one turn of the browser's JavaScript cycle and that's just long enough. for one turn of the browser's JavaScript cycle and that's just long enough.
Here's *AfterView* in action: Here's *AfterView* in action:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -714,6 +788,7 @@ Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afte
This time, instead of including the child view within the template, it imports the content from This time, instead of including the child view within the template, it imports the content from
the `AfterContentComponent`'s parent. Here's the parent's template: the `AfterContentComponent`'s parent. Here's the parent's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false">
</code-example> </code-example>
@ -724,6 +799,7 @@ into the component*.
Now look at the component's template: Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -731,6 +807,7 @@ Now look at the component's template:
The `<ng-content>` tag is a *placeholder* for the external content. The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content. It tells Angular where to insert that content.
In this case, the projected content is the `<my-child>` from the parent. In this case, the projected content is the `<my-child>` from the parent.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -763,6 +840,7 @@ which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/index/ContentChild-decorator). [@ContentChild](api/core/index/ContentChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false">
</code-example> </code-example>

View File

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

View File

@ -63,6 +63,7 @@ General
* [What is the Angular compiler?](guide/ngmodule-faq#q-angular-compiler) * [What is the Angular compiler?](guide/ngmodule-faq#q-angular-compiler)
* [Can you summarize the NgModule API?](guide/ngmodule-faq#q-ngmodule-api) * [Can you summarize the NgModule API?](guide/ngmodule-faq#q-ngmodule-api)
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -78,6 +79,7 @@ Add [declarable](guide/ngmodule-faq#q-declarable) classes&mdash;components, dire
Declare these classes in _exactly one_ module of the application. Declare these classes in _exactly one_ module of the application.
Declare them in _this_ module if they _belong_ to this module. Declare them in _this_ module if they _belong_ to this module.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -92,6 +94,7 @@ Declarables are the class types&mdash;components, directives, and pipes&mdash;th
you can add to a module's `declarations` list. you can add to a module's `declarations` list.
They're the _only_ classes that you can add to `declarations`. They're the _only_ classes that you can add to `declarations`.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -113,6 +116,7 @@ For example, don't declare FORMS_DIRECTIVES from `@angular/forms`.
* Non-Angular classes and objects, such as * Non-Angular classes and objects, such as
strings, numbers, functions, entity models, configurations, business logic, and helper classes. strings, numbers, functions, entity models, configurations, business logic, and helper classes.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -135,6 +139,7 @@ Membership in one list doesn't imply membership in another list.
* `HeroComponent` could be exported for inclusion in an external component's template * `HeroComponent` could be exported for inclusion in an external component's template
as well as dynamically loaded in a pop-up dialog. as well as dynamically loaded in a pop-up dialog.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -153,6 +158,7 @@ For example, if "x" is `ngModel`, you probably haven't imported the `FormsModule
Perhaps you declared "x" in an application sub-module but forgot to export it? Perhaps you declared "x" in an application sub-module but forgot to export it?
The "x" class isn't visible to other modules until you add it to the `exports` list. The "x" class isn't visible to other modules until you add it to the `exports` list.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -178,6 +184,7 @@ components, directives, and pipes.
Import only [BrowserModule](guide/ngmodule-faq#q-browser-vs-common-module) in the root `AppModule`. Import only [BrowserModule](guide/ngmodule-faq#q-browser-vs-common-module) in the root `AppModule`.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -209,6 +216,7 @@ They need the common directives. They don't need to re-install the app-wide prov
Importing `CommonModule` also frees feature modules for use on _any_ target platform, not just browsers. Importing `CommonModule` also frees feature modules for use on _any_ target platform, not just browsers.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -229,6 +237,7 @@ When Angular gets to the 'B' and 'A' in 'D', they're already cached and ready to
Angular doesn't like modules with circular references, so don't let Module 'A' import Module 'B', which imports Module 'A'. Angular doesn't like modules with circular references, so don't let Module 'A' import Module 'B', which imports Module 'A'.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -250,6 +259,7 @@ it's declared in this module or in an imported module.
You _can_ re-export entire imported modules, which effectively re-exports all of their exported classes. You _can_ re-export entire imported modules, which effectively re-exports all of their exported classes.
A module can even export a module that it doesn't import. A module can even export a module that it doesn't import.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -272,6 +282,7 @@ While there's no harm in exporting them, there's also no benefit.
For example, there's no point in re-exporting `HttpModule` because it doesn't export anything. For example, there's no point in re-exporting `HttpModule` because it doesn't export anything.
It's only purpose is to add http service providers to the application as a whole. It's only purpose is to add http service providers to the application as a whole.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -292,6 +303,7 @@ re-export them in a consolidated, convenience module.
A module can re-export entire modules, which effectively re-exports all of their exported classes. A module can re-export entire modules, which effectively re-exports all of their exported classes.
Angular's own `BrowserModule` exports a couple of modules like this: Angular's own `BrowserModule` exports a couple of modules like this:
<code-example> <code-example>
exports: [CommonModule, ApplicationModule] exports: [CommonModule, ApplicationModule]
@ -310,6 +322,7 @@ It's only purpose is to add http service providers to the application as a whole
~~~ ~~~
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -344,6 +357,7 @@ configure services in root and feature modules respectively.
Angular doesn't recognize these names but Angular developers do. Angular doesn't recognize these names but Angular developers do.
Follow this convention when you write similar modules with configurable service providers. Follow this convention when you write similar modules with configurable service providers.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -375,6 +389,7 @@ If the `HeroModule` provides the `HeroService` and the root `AppModule` imports
any class that knows the `HeroService` _type_ can inject that service, any class that knows the `HeroService` _type_ can inject that service,
not just the classes declared in the `HeroModule`. not just the classes declared in the `HeroModule`.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -398,6 +413,7 @@ These providers are insulated from changes to application providers with the sam
When the router creates a component within the lazy-loaded context, When the router creates a component within the lazy-loaded context,
Angular prefers service instances created from these providers to the service instances of the application root injector. Angular prefers service instances created from these providers to the service instances of the application root injector.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -423,6 +439,7 @@ that also provides a service for token 'X', then Module A's service definition "
The service provided by the root `AppModule` takes precedence over services provided by imported modules. The service provided by the root `AppModule` takes precedence over services provided by imported modules.
The `AppModule` always wins. The `AppModule` always wins.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -483,6 +500,7 @@ You can embed the child components in the top component's template.
Alternatively, make the top component a routing host by giving it a `<router-outlet>`. Alternatively, make the top component a routing host by giving it a `<router-outlet>`.
Define child routes and let the router load module components into that outlet. Define child routes and let the router load module components into that outlet.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -524,6 +542,7 @@ In the NgModule page sample applications, if you had registered `UserService` in
the `HeroComponent` couldn't inject it. the `HeroComponent` couldn't inject it.
The application would fail the moment a user navigated to "Heroes". The application would fail the moment a user navigated to "Heroes".
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -549,6 +568,7 @@ The changes that editor makes to heroes in its service don't touch the hero inst
[Always register _application-wide_ services with the root `AppModule`](guide/ngmodule-faq#q-root-component-or-module), [Always register _application-wide_ services with the root `AppModule`](guide/ngmodule-faq#q-root-component-or-module),
not the root `AppComponent`. not the root `AppComponent`.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -598,6 +618,7 @@ The username goes bonkers as the Angular creates a new `UserService` instance ea
~~~ ~~~
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -629,6 +650,7 @@ Angular must add the lazy-loaded module's providers to an injector somewhere.
It can't added them to the app root injector because that injector is closed to new providers. It can't added them to the app root injector because that injector is closed to new providers.
So Angular creates a new child injector for the lazy-loaded module context. So Angular creates a new child injector for the lazy-loaded module context.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -650,11 +672,13 @@ You can throw an error or take other remedial action.
Certain NgModules (such as `BrowserModule`) implement such a guard, Certain NgModules (such as `BrowserModule`) implement such a guard,
such as this `CoreModule` constructor from the NgModules page. such as this `CoreModule` constructor from the NgModules page.
<code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false">
</code-example> </code-example>
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -698,6 +722,7 @@ Angular automatically adds the following types of components to the module's `en
You don't have to mention these components explicitly, although doing so is harmless. You don't have to mention these components explicitly, although doing so is harmless.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -718,6 +743,7 @@ it should generate code to bootstrap the application with this component.
There's no need to list a component in both the `bootstrap` and `entryComponent` lists, There's no need to list a component in both the `bootstrap` and `entryComponent` lists,
although doing so is harmless. although doing so is harmless.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -743,6 +769,7 @@ it's best to add only the components that are truly _entry components_.
Don't include components that [are referenced](guide/ngmodule-faq#q-template-reference) Don't include components that [are referenced](guide/ngmodule-faq#q-template-reference)
in the templates of other components. in the templates of other components.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -778,6 +805,7 @@ If a component isn't an _entry component_ or wasn't found in a template,
the compiler omits it. the compiler omits it.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -854,15 +882,19 @@ follow them unless you have a good reason to do otherwise.
~~~ ~~~
<table> <table>
<tr> <tr>
<th style="vertical-align: top"> <th style="vertical-align: top">
Feature Module Feature Module
</th> </th>
<th style="vertical-align: top"> <th style="vertical-align: top">
Guidelines Guidelines
</th> </th>
@ -871,13 +903,16 @@ follow them unless you have a good reason to do otherwise.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="domain-feature-module"></a>Domain <a id="domain-feature-module"></a>Domain
</td> </td>
<td> <td>
Domain feature modules deliver a user experience *dedicated to a particular application domain* Domain feature modules deliver a user experience *dedicated to a particular application domain*
like editing a customer or placing an order. like editing a customer or placing an order.
@ -913,13 +948,16 @@ follow them unless you have a good reason to do otherwise.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="routed-feature-module"></a>Routed <a id="routed-feature-module"></a>Routed
</td> </td>
<td> <td>
_Routed feature modules_ are _domain feature modules_ _Routed feature modules_ are _domain feature modules_
whose top components are the *targets of router navigation routes*. whose top components are the *targets of router navigation routes*.
@ -951,13 +989,16 @@ follow them unless you have a good reason to do otherwise.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="routing-module"></a>Routing <a id="routing-module"></a>Routing
</td> </td>
<td> <td>
A [routing module](guide/router) *provides routing configuration* for another module. A [routing module](guide/router) *provides routing configuration* for another module.
@ -1004,13 +1045,16 @@ follow them unless you have a good reason to do otherwise.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="service-feature-module"></a>Service <a id="service-feature-module"></a>Service
</td> </td>
<td> <td>
Service modules *provide utility services* such as data access and messaging. Service modules *provide utility services* such as data access and messaging.
@ -1027,13 +1071,16 @@ follow them unless you have a good reason to do otherwise.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="widget-feature-module"></a>Widget <a id="widget-feature-module"></a>Widget
</td> </td>
<td> <td>
A widget module makes *components, directives, and pipes* available to external modules. A widget module makes *components, directives, and pipes* available to external modules.
@ -1064,35 +1111,43 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
~~~ ~~~
<table> <table>
<tr> <tr>
<th> <th>
Feature Module Feature Module
</th> </th>
<th> <th>
Declarations Declarations
</th> </th>
<th> <th>
Providers Providers
</th> </th>
<th> <th>
Exports Exports
</th> </th>
<th> <th>
Imported By Imported By
</th> </th>
<th> <th>
Examples Examples
</th> </th>
@ -1101,33 +1156,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr> </tr>
<tr> <tr>
<td> <td>
Domain Domain
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
Top component Top component
</td> </td>
<td> <td>
Feature, <code>AppModule</code> Feature, <code>AppModule</code>
</td> </td>
<td> <td>
<code>ContactModule</code> (before routing) <code>ContactModule</code> (before routing)
</td> </td>
@ -1136,33 +1198,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr> </tr>
<tr> <tr>
<td> <td>
Routed Routed
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Nobody Nobody
</td> </td>
<td> <td>
<code>ContactModule</code>, <code>HeroModule</code>, <code>CrisisModule</code> <code>ContactModule</code>, <code>HeroModule</code>, <code>CrisisModule</code>
</td> </td>
@ -1171,33 +1240,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr> </tr>
<tr> <tr>
<td> <td>
Routing Routing
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Yes (Guards) Yes (Guards)
</td> </td>
<td> <td>
<code>RouterModule</code> <code>RouterModule</code>
</td> </td>
<td> <td>
Feature (for routing) Feature (for routing)
</td> </td>
<td> <td>
<code>AppRoutingModule</code>, <code>ContactRoutingModule</code>, <code>HeroRoutingModule</code> <code>AppRoutingModule</code>, <code>ContactRoutingModule</code>, <code>HeroRoutingModule</code>
</td> </td>
@ -1206,33 +1282,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr> </tr>
<tr> <tr>
<td> <td>
Service Service
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
<code>AppModule</code> <code>AppModule</code>
</td> </td>
<td> <td>
<code>HttpModule</code>, <code>CoreModule</code> <code>HttpModule</code>, <code>CoreModule</code>
</td> </td>
@ -1241,33 +1324,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr> </tr>
<tr> <tr>
<td> <td>
Widget Widget
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Feature Feature
</td> </td>
<td> <td>
<code>CommonModule</code>, <code>SharedModule</code> <code>CommonModule</code>, <code>SharedModule</code>
</td> </td>
@ -1279,6 +1369,7 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</table> </table>
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1295,6 +1386,7 @@ In modern JavaScript, every file is a _module_
(see the [Modules](http://exploringjs.com/es6/ch_modules.html) page of the Exploring ES6 website). (see the [Modules](http://exploringjs.com/es6/ch_modules.html) page of the Exploring ES6 website).
Within each file you write an `export` statement to make parts of the module public: Within each file you write an `export` statement to make parts of the module public:
<code-example format='.'> <code-example format='.'>
export class AppComponent { ... } export class AppComponent { ... }
@ -1302,6 +1394,7 @@ Within each file you write an `export` statement to make parts of the module pub
Then you `import` a part in another module: Then you `import` a part in another module:
<code-example format='.'> <code-example format='.'>
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -1342,17 +1435,20 @@ They are available _everywhere_.
Here's an _NgModule_ class with imports, exports, and declarations. Here's an _NgModule_ class with imports, exports, and declarations.
<code-example path="ngmodule/src/app/contact/contact.module.2.ts" region="class" linenums="false"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts" region="class" linenums="false">
</code-example> </code-example>
Of course you use _JavaScript_ modules to write _Angular_ modules as seen in the complete `contact.module.ts` file: Of course you use _JavaScript_ modules to write _Angular_ modules as seen in the complete `contact.module.ts` file:
<code-example path="ngmodule/src/app/contact/contact.module.2.ts" linenums="false"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts" linenums="false">
</code-example> </code-example>
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1374,6 +1470,7 @@ The compiler finds a pipe if the pipe's *name* appears within the pipe syntax of
Angular only matches selectors and pipe names for classes that are declared by this module Angular only matches selectors and pipe names for classes that are declared by this module
or exported by a module that this module imports. or exported by a module that this module imports.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1405,6 +1502,7 @@ the Angular compiler incorporates them into compiled component code too.
`@NgModule` metadata tells the Angular compiler what components to compile for this module and `@NgModule` metadata tells the Angular compiler what components to compile for this module and
how to link this module with other modules. how to link this module with other modules.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1416,15 +1514,19 @@ how to link this module with other modules.
## NgModule API ## NgModule API
The following table summarizes the `NgModule` metadata properties. The following table summarizes the `NgModule` metadata properties.
<table> <table>
<tr> <tr>
<th> <th>
Property Property
</th> </th>
<th> <th>
Description Description
</th> </th>
@ -1433,13 +1535,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>declarations</code> <code>declarations</code>
</td> </td>
<td> <td>
A list of [declarable](guide/ngmodule-faq#q-declarable) classes, A list of [declarable](guide/ngmodule-faq#q-declarable) classes,
the *component*, *directive*, and *pipe* classes that _belong to this module_. the *component*, *directive*, and *pipe* classes that _belong to this module_.
@ -1459,13 +1564,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>providers</code> <code>providers</code>
</td> </td>
<td> <td>
A list of dependency-injection providers. A list of dependency-injection providers.
@ -1492,13 +1600,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>imports</code> <code>imports</code>
</td> </td>
<td> <td>
A list of supporting modules. A list of supporting modules.
@ -1521,13 +1632,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>exports</code> <code>exports</code>
</td> </td>
<td> <td>
A list of declarations&mdash;*component*, *directive*, and *pipe* classes&mdash;that A list of declarations&mdash;*component*, *directive*, and *pipe* classes&mdash;that
an importing module can use. an importing module can use.
@ -1539,7 +1653,7 @@ The following table summarizes the `NgModule` metadata properties.
Declarations are private by default. Declarations are private by default.
If this module does _not_ export `HeroComponent`, no other module can see it. If this module does _not_ export `HeroComponent`, no other module can see it.
Importing a module does _not_ automatically re-export the imported module's exports. Importing a module does _not_ automatically re-export the imported module's imports.
Module 'B' can't use `ngIf` just because it imported module `A` which imported `CommonModule`. Module 'B' can't use `ngIf` just because it imported module `A` which imported `CommonModule`.
Module 'B' must import `CommonModule` itself. Module 'B' must import `CommonModule` itself.
@ -1556,13 +1670,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>bootstrap</code> <code>bootstrap</code>
</td> </td>
<td> <td>
A list of components that can be bootstrapped. A list of components that can be bootstrapped.
@ -1579,13 +1696,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>entryComponents</code> <code>entryComponents</code>
</td> </td>
<td> <td>
A list of components that are _not_ [referenced](guide/ngmodule-faq#q-template-reference) in a reachable component template. A list of components that are _not_ [referenced](guide/ngmodule-faq#q-template-reference) in a reachable component template.

View File

@ -66,6 +66,7 @@ The companion [NgModule FAQs](cookbook/ngmodule-faq) cookbook
offers answers to specific design and implementation questions. offers answers to specific design and implementation questions.
Read this page before reading those FAQs. Read this page before reading those FAQs.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -122,6 +123,7 @@ By convention, the *root module* class is called `AppModule` and it exists in a
The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible: The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible:
<code-example path="setup/src/app/app.module.ts" linenums="false"> <code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example> </code-example>
@ -140,6 +142,7 @@ the _root component_, the top of the app's rather bare component tree.
The example `AppComponent` simply displays a data-bound title: The example `AppComponent` simply displays a data-bound title:
<code-example path="ngmodule/src/app/app.component.0.ts" linenums="false"> <code-example path="ngmodule/src/app/app.component.0.ts" linenums="false">
</code-example> </code-example>
@ -162,6 +165,7 @@ In the first, _dynamic_ option, the [Angular compiler](cookbook/ngmodule-faq)
compiles the application in the browser and then launches the app. compiles the application in the browser and then launches the app.
<code-example path="ngmodule/src/main.ts" linenums="false"> <code-example path="ngmodule/src/main.ts" linenums="false">
</code-example> </code-example>
@ -184,6 +188,7 @@ The syntax for bootstrapping the pre-compiled `AppModuleNgFactory` is similar to
the dynamic version that bootstraps the `AppModule` class. the dynamic version that bootstraps the `AppModule` class.
<code-example path="ngmodule/src/main-static.ts" linenums="false"> <code-example path="ngmodule/src/main-static.ts" linenums="false">
</code-example> </code-example>
@ -205,6 +210,7 @@ In general, the `AppModule` should neither know nor care how it is bootstrapped.
Although the `AppModule` evolves as the app grows, the bootstrap code in `main.ts` doesn't change. Although the `AppModule` evolves as the app grows, the bootstrap code in `main.ts` doesn't change.
This is the last time you'll look at `main.ts`. This is the last time you'll look at `main.ts`.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -218,12 +224,14 @@ As the app evolves,
the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives) the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives)
that sets the background color of the attached element. that sets the background color of the attached element.
<code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false"> <code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false">
</code-example> </code-example>
Update the `AppComponent` template to attach the directive to the title: Update the `AppComponent` template to attach the directive to the title:
<code-example path="ngmodule/src/app/app.component.1.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.1.ts" region="template" linenums="false">
</code-example> </code-example>
@ -233,6 +241,7 @@ You must declare the directive in `AppModule`.
Import the `HighlightDirective` class and add it to the module's `declarations` like this: Import the `HighlightDirective` class and add it to the module's `declarations` like this:
<code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false">
</code-example> </code-example>
@ -242,12 +251,14 @@ Import the `HighlightDirective` class and add it to the module's `declarations`
Refactor the title into its own `TitleComponent`. Refactor the title into its own `TitleComponent`.
The component's template binds to the component's `title` and `subtitle` properties like this: The component's template binds to the component's `title` and `subtitle` properties like this:
<code-example path="ngmodule/src/app/title.component.html" region="v1" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" region="v1" linenums="false">
</code-example> </code-example>
<code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false"> <code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -255,6 +266,7 @@ The component's template binds to the component's `title` and `subtitle` propert
Rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element, Rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element,
using an input binding to set the `subtitle`. using an input binding to set the `subtitle`.
<code-example path="ngmodule/src/app/app.component.1.ts" linenums="false"> <code-example path="ngmodule/src/app/app.component.1.ts" linenums="false">
</code-example> </code-example>
@ -262,6 +274,7 @@ using an input binding to set the `subtitle`.
Angular won't recognize the `<app-title>` tag until you declare it in `AppModule`. Angular won't recognize the `<app-title>` tag until you declare it in `AppModule`.
Import the `TitleComponent` class and add it to the module's `declarations`: Import the `TitleComponent` class and add it to the module's `declarations`:
<code-example path="ngmodule/src/app/app.module.1.ts" region="component" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="component" linenums="false">
</code-example> </code-example>
@ -287,6 +300,7 @@ accessible through a user service.
This sample application has a dummy implementation of such a `UserService`. This sample application has a dummy implementation of such a `UserService`.
<code-example path="ngmodule/src/app/user.service.ts" linenums="false"> <code-example path="ngmodule/src/app/user.service.ts" linenums="false">
</code-example> </code-example>
@ -294,6 +308,7 @@ This sample application has a dummy implementation of such a `UserService`.
The sample application should display a welcome message to the logged-in user just below the application title. The sample application should display a welcome message to the logged-in user just below the application title.
Update the `TitleComponent` template to show the welcome message below the application title. Update the `TitleComponent` template to show the welcome message below the application title.
<code-example path="ngmodule/src/app/title.component.html" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" linenums="false">
</code-example> </code-example>
@ -301,6 +316,7 @@ Update the `TitleComponent` template to show the welcome message below the appli
Update the `TitleComponent` class with a constructor that injects the `UserService` Update the `TitleComponent` class with a constructor that injects the `UserService`
and sets the component's `user` property from the service. and sets the component's `user` property from the service.
<code-example path="ngmodule/src/app/title.component.ts" linenums="false"> <code-example path="ngmodule/src/app/title.component.ts" linenums="false">
</code-example> </code-example>
@ -308,6 +324,7 @@ and sets the component's `user` property from the service.
You've defined and used the service. Now to _provide_ it for all components to use, You've defined and used the service. Now to _provide_ it for all components to use,
add it to a `providers` property in the `AppModule` metadata: add it to a `providers` property in the `AppModule` metadata:
<code-example path="ngmodule/src/app/app.module.1.ts" region="providers" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -321,6 +338,7 @@ add it to a `providers` property in the `AppModule` metadata:
In the revised `TitleComponent`, an `*ngIf` directive guards the message. In the revised `TitleComponent`, an `*ngIf` directive guards the message.
There is no message if there is no user. There is no message if there is no user.
<code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false">
</code-example> </code-example>
@ -331,6 +349,7 @@ How can that be? The Angular compiler should either ignore or complain about unr
Angular does recognize `NgIf` because you imported it earlier. Angular does recognize `NgIf` because you imported it earlier.
The initial version of `AppModule` imports `BrowserModule`. The initial version of `AppModule` imports `BrowserModule`.
<code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -384,6 +403,7 @@ import the `ReactiveFormsModule`.
The `ContactComponent` selector matches an element named `<app-contact>`. The `ContactComponent` selector matches an element named `<app-contact>`.
Add an element with that name to the `AppComponent` template, just below the `<app-title>`: Add an element with that name to the `AppComponent` template, just below the `<app-title>`:
<code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false">
</code-example> </code-example>
@ -395,33 +415,40 @@ and an alternative version of the `HighlightDirective`.
To make it manageable, place all contact-related material in an `src/app/contact` folder To make it manageable, place all contact-related material in an `src/app/contact` folder
and break the component into three constituent HTML, TypeScript, and css files: and break the component into three constituent HTML, TypeScript, and css files:
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html"> <code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts"> <code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css"> <code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts"> <code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts"> <code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts"> <code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane> </code-pane>
@ -445,6 +472,7 @@ form features such as validation aren't yet available.
Add the `FormsModule` to the `AppModule` metadata's `imports` list. Add the `FormsModule` to the `AppModule` metadata's `imports` list.
<code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -474,6 +502,7 @@ Components, directives, and pipes belong to _one module only_.
The application won't compile until you declare the contact component, directive, and pipe. The application won't compile until you declare the contact component, directive, and pipe.
Update the `declarations` in the `AppModule` accordingly: Update the `declarations` in the `AppModule` accordingly:
<code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false">
</code-example> </code-example>
@ -489,6 +518,7 @@ There are two directives with the same name, both called `HighlightDirective`.
To work around this, create an alias for the contact version using the `as` JavaScript import keyword. To work around this, create an alias for the contact version using the `as` JavaScript import keyword.
<code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false"> <code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false">
</code-example> </code-example>
@ -511,6 +541,7 @@ You want to share this service with other contact-related components that you'll
In this app, add `ContactService` to the `AppModule` metadata's `providers` list: In this app, add `ContactService` to the `AppModule` metadata's `providers` list:
<code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false"> <code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -553,52 +584,64 @@ Now you can inject `ContactService` (like `UserService`) into any component in t
Everything is in place to run the application with its contact editor. Everything is in place to run the application with its contact editor.
The app file structure looks like this: The app file structure looks like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
title.component.(html|ts) title.component.(html|ts)
</aio-file> </aio-file>
<aio-file> <aio-file>
user.service.ts user.service.ts
</aio-file> </aio-file>
<aio-folder> <aio-folder>
contact contact
<aio-file> <aio-file>
awesome.pipe.ts awesome.pipe.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
contact.component.(css|html|ts) contact.component.(css|html|ts)
</aio-file> </aio-file>
<aio-file> <aio-file>
contact.service.ts contact.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
@ -628,13 +671,16 @@ you already had a `HighlightDirective` class at the application level.
The selectors of the two directives both highlight the attached element with a different color. The selectors of the two directives both highlight the attached element with a different color.
<code-tabs> <code-tabs>
<code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts"> <code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts"> <code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane> </code-pane>
@ -736,6 +782,7 @@ It's easy to refactor the contact material into a contact feature module.
Here's the new `ContactModule`: Here's the new `ContactModule`:
<code-example path="ngmodule/src/app/contact/contact.module.2.ts"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts">
</code-example> </code-example>
@ -778,13 +825,16 @@ Then import the `ContactModule` so the app can continue to display the exported
Here's the refactored version of the `AppModule` along with the previous version. Here's the refactored version of the `AppModule` along with the previous version.
<code-tabs> <code-tabs>
<code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts"> <code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts"> <code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts">
</code-pane> </code-pane>
@ -835,6 +885,7 @@ Some facets of the current application merit discussion are as follows:
The new `AppComponent` template has The new `AppComponent` template has
a title, three links, and a `<router-outlet>`. a title, three links, and a `<router-outlet>`.
<code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false">
</code-example> </code-example>
@ -843,6 +894,7 @@ The `<app-contact>` element is gone; you're routing to the _Contact_ page now.
The `AppModule` has changed modestly: The `AppModule` has changed modestly:
<code-example path="ngmodule/src/app/app.module.3.ts"> <code-example path="ngmodule/src/app/app.module.3.ts">
</code-example> </code-example>
@ -870,6 +922,7 @@ that handles the app's routing concerns.
### App routing ### App routing
<code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false">
</code-example> </code-example>
@ -889,6 +942,7 @@ You'll get to that file in a moment.
The remaining two routes use lazy loading syntax to tell the router where to find the modules: The remaining two routes use lazy loading syntax to tell the router where to find the modules:
<code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false">
</code-example> </code-example>
@ -909,6 +963,7 @@ the latter separated from the former by a `#`.
The `forRoot` static class method of the `RouterModule` with the provided configuration and The `forRoot` static class method of the `RouterModule` with the provided configuration and
added to the `imports` array provides the routing concerns for the module. added to the `imports` array provides the routing concerns for the module.
<code-example path="ngmodule/src/app/app-routing.module.ts" region="forRoot" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" region="forRoot" linenums="false">
</code-example> </code-example>
@ -928,6 +983,7 @@ Never call `RouterModule.forRoot` in a feature-routing module.
Back in the root `AppModule`, add the `AppRoutingModule` to its `imports` list, Back in the root `AppModule`, add the `AppRoutingModule` to its `imports` list,
and the app is ready to navigate. and the app is ready to navigate.
<code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -936,6 +992,7 @@ and the app is ready to navigate.
The `src/app/contact` folder holds a new file, `contact-routing.module.ts`. The `src/app/contact` folder holds a new file, `contact-routing.module.ts`.
It defines the `contact` route mentioned earlier and provides a `ContactRoutingModule` as follows: It defines the `contact` route mentioned earlier and provides a `ContactRoutingModule` as follows:
<code-example path="ngmodule/src/app/contact/contact-routing.module.ts" region="routing" linenums="false"> <code-example path="ngmodule/src/app/contact/contact-routing.module.ts" region="routing" linenums="false">
</code-example> </code-example>
@ -967,13 +1024,16 @@ that has both shared [declarables](cookbook/ngmodule-faq) and services.
`ContactModule` has changed in two small but important ways. `ContactModule` has changed in two small but important ways.
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.module.3.ts" path="ngmodule/src/app/contact/contact.module.3.ts" region="class"> <code-pane title="src/app/contact/contact.module.3.ts" path="ngmodule/src/app/contact/contact.module.3.ts" region="class">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class"> <code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class">
</code-pane> </code-pane>
@ -999,40 +1059,49 @@ They don't look different from the eagerly loaded `ContactModule`.
The `HeroModule` is a bit more complex than the `CrisisModule`, which makes it The `HeroModule` is a bit more complex than the `CrisisModule`, which makes it
a more interesting and useful example. Its file structure is as follows: a more interesting and useful example. Its file structure is as follows:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
hero hero
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-list.component.ts hero-list.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.component.ts hero.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.module.ts hero.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-routing.module.ts hero-routing.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
@ -1056,6 +1125,7 @@ In the next section, [Shared modules](guide/ngmodule#shared-module "Shared modul
The `HeroModule` is a feature module like any other. The `HeroModule` is a feature module like any other.
<code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false"> <code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false">
</code-example> </code-example>
@ -1087,6 +1157,7 @@ and share them with the modules that need them.
Here is the `SharedModule`: Here is the `SharedModule`:
<code-example path="ngmodule/src/app/shared/shared.module.ts"> <code-example path="ngmodule/src/app/shared/shared.module.ts">
</code-example> </code-example>
@ -1166,6 +1237,7 @@ Perform the following steps:
Most of this work is familiar. The interesting part is the `CoreModule`. Most of this work is familiar. The interesting part is the `CoreModule`.
<code-example path="ngmodule/src/app/core/core.module.ts" region="v4"> <code-example path="ngmodule/src/app/core/core.module.ts" region="v4">
</code-example> </code-example>
@ -1227,13 +1299,16 @@ Having refactored to a `CoreModule` and a `SharedModule`, it's time to clean up
Here is the updated `AppModule` paired with version 3 for comparison: Here is the updated `AppModule` paired with version 3 for comparison:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4"> <code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts"> <code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts">
</code-pane> </code-pane>
@ -1250,13 +1325,16 @@ Here is the updated `AppModule` paired with version 3 for comparison:
### A trimmer _ContactModule_ ### A trimmer _ContactModule_
Here is the new `ContactModule` paired with the prior version: Here is the new `ContactModule` paired with the prior version:
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts"> <code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts"> <code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts">
</code-pane> </code-pane>
@ -1269,6 +1347,7 @@ Notice the following:
* The imports include `SharedModule` instead of `CommonModule` and `FormsModule`. * The imports include `SharedModule` instead of `CommonModule` and `FormsModule`.
* The new version is leaner and cleaner. * The new version is leaner and cleaner.
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1303,18 +1382,21 @@ Add a `CoreModule.forRoot` method that configures the core `UserService`.
You've extended the core `UserService` with an optional, injected `UserServiceConfig`. You've extended the core `UserService` with an optional, injected `UserServiceConfig`.
If a `UserServiceConfig` exists, the `UserService` sets the user name from that config. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
<code-example path="ngmodule/src/app/core/user.service.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/user.service.ts" region="ctor" linenums="false">
</code-example> </code-example>
Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object: Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object:
<code-example path="ngmodule/src/app/core/core.module.ts" region="for-root" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="for-root" linenums="false">
</code-example> </code-example>
Lastly, call it within the `imports` list of the `AppModule`. Lastly, call it within the `imports` list of the `AppModule`.
<code-example path="ngmodule/src/app/app.module.ts" region="import-for-root" linenums="false"> <code-example path="ngmodule/src/app/app.module.ts" region="import-for-root" linenums="false">
</code-example> </code-example>
@ -1334,6 +1416,7 @@ Remember to _import_ the result; don't add it to any other `@NgModule` list.
~~~ ~~~
<div class='l-hr'> <div class='l-hr'>
</div> </div>
@ -1352,6 +1435,7 @@ It looks like it is supposed to go to a specific question/section within the pag
You could hope that no developer makes that mistake. You could hope that no developer makes that mistake.
Or you can guard against it and fail fast by adding the following `CoreModule` constructor. Or you can guard against it and fail fast by adding the following `CoreModule` constructor.
<code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false">
</code-example> </code-example>

View File

@ -53,6 +53,7 @@ The `package.json` includes two sets of packages,
The *dependencies* are essential to *running* the application. The *dependencies* are essential to *running* the application.
The *devDependencies* are only necessary to *develop* the application. The *devDependencies* are only necessary to *develop* the application.
You can exclude them from production installations by adding `--production` to the install command, as follows: You can exclude them from production installations by adding `--production` to the install command, as follows:
<code-example format="." language="bash"> <code-example format="." language="bash">
npm install my-application --production npm install my-application --production

View File

@ -31,6 +31,7 @@ In this page, you'll use pipes to transform a component's birthday property into
a human-friendly date. a human-friendly date.
<code-example path="pipes/src/app/hero-birthday1.component.ts" linenums="false"> <code-example path="pipes/src/app/hero-birthday1.component.ts" linenums="false">
</code-example> </code-example>
@ -38,6 +39,7 @@ a human-friendly date.
Focus on the component's template. Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false">
</code-example> </code-example>
@ -52,6 +54,7 @@ function on the right. All pipes work this way.
The `Date` and `Currency` pipes need the *ECMAScript Internationalization API*. The `Date` and `Currency` pipes need the *ECMAScript Internationalization API*.
Safari and other older browsers don't support it. You can add support with a polyfill. Safari and other older browsers don't support it. You can add support with a polyfill.
<code-example language="html"> <code-example language="html">
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt; &lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt;
@ -89,6 +92,7 @@ Modify the birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**: After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
<code-example path="pipes/src/app/app.component.html" region="format-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="format-birthday" linenums="false">
</code-example> </code-example>
@ -103,6 +107,7 @@ Write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component: to the component's `format` property. Here's the template for that component:
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" linenums="false"> <code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -112,6 +117,7 @@ That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`). (`'shortDate'`) and a longer form (`'fullDate'`).
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" linenums="false"> <code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -120,6 +126,7 @@ As you click the button, the displayed date alternates between
"**<samp>04/15/1988</samp>**" and "**<samp>04/15/1988</samp>**" and
"**<samp>Friday, April 15, 1988</samp>**". "**<samp>Friday, April 15, 1988</samp>**".
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"> </img> <img src='assets/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"> </img>
</figure> </figure>
@ -142,6 +149,7 @@ the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
The birthday displays as **<samp>APR 15, 1988</samp>**. The birthday displays as **<samp>APR 15, 1988</samp>**.
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="chained-birthday" linenums="false">
</code-example> </code-example>
@ -150,6 +158,7 @@ This example&mdash;which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**&mdash;
the same pipes as above, but passes in a parameter to `date` as well. the same pipes as above, but passes in a parameter to `date` as well.
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" linenums="false">
</code-example> </code-example>
@ -161,6 +170,7 @@ You can write your own custom pipes.
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers: Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
<code-example path="pipes/src/app/exponential-strength.pipe.ts" linenums="false"> <code-example path="pipes/src/app/exponential-strength.pipe.ts" linenums="false">
</code-example> </code-example>
@ -192,11 +202,13 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
Now you need a component to demonstrate the pipe. Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" linenums="false"> <code-example path="pipes/src/app/power-booster.component.ts" linenums="false">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/pipes/power-booster.png' alt="Power Booster"> </img> <img src='assets/images/devguide/pipes/power-booster.png' alt="Power Booster"> </img>
</figure> </figure>
@ -210,6 +222,7 @@ Note the following:
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
Remember the Remember the
</header> </header>
@ -232,11 +245,13 @@ Upgrade the example to a "Power Boost Calculator" that combines
your pipe and two-way data binding with `ngModel`. your pipe and two-way data binding with `ngModel`.
<code-example path="pipes/src/app/power-boost-calculator.component.ts"> <code-example path="pipes/src/app/power-boost-calculator.component.ts">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"> </img> <img src='assets/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"> </img>
</figure> </figure>
@ -259,12 +274,14 @@ In the next example, the component uses the default, aggressive change detection
its display of every hero in the `heroes` #{_array}. Here's the template: its display of every hero in the `heroes` #{_array}. Here's the template:
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" linenums="false">
</code-example> </code-example>
The companion component class provides heroes, adds heroes into the #{_array}, and can reset the #{_array}. The companion component class provides heroes, adds heroes into the #{_array}, and can reset the #{_array}.
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -277,12 +294,14 @@ If you added the ability to remove or change a hero, Angular would detect those
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly. Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" linenums="false">
</code-example> </code-example>
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier. Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" linenums="false"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" linenums="false">
</code-example> </code-example>
@ -295,6 +314,7 @@ It's just using a different change-detection algorithm that ignores changes to t
Notice how a hero is added: Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false">
</code-example> </code-example>
@ -311,6 +331,7 @@ if you *replace* the #{_array}, the pipe executes and the display is updated.
The Flying Heroes application extends the The Flying Heroes application extends the
code with checkbox switches and additional displays to help you experience these effects. code with checkbox switches and additional displays to help you experience these effects.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img> <img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img>
</figure> </figure>
@ -338,6 +359,7 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
impure like this: impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false">
</code-example> </code-example>
@ -385,13 +407,16 @@ An expensive, long-running pipe could destroy the user experience.
A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`. A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
The complete implementation is as follows: The complete implementation is as follows:
<code-tabs> <code-tabs>
<code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure"> <code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure">
</code-pane> </code-pane>
<code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure"> <code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure">
</code-pane> </code-pane>
@ -405,6 +430,7 @@ The only difference is the `pure` flag in the pipe metadata.
This is a good candidate for an impure pipe because the `transform` function is trivial and fast. This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" title="src/app/flying-heroes.pipe.ts (filter)" region="filter"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" title="src/app/flying-heroes.pipe.ts (filter)" region="filter">
</code-example> </code-example>
@ -412,6 +438,7 @@ This is a good candidate for an impure pipe because the `transform` function is
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`. You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
<code-example path="pipes/src/app/flying-heroes" linenums="false" title="src/app/flying-heroes (_region)" region="_region"> <code-example path="pipes/src/app/flying-heroes" linenums="false" title="src/app/flying-heroes (_region)" region="_region">
</code-example> </code-example>
@ -420,6 +447,7 @@ The only substantive change is the pipe in the template.
You can confirm in the <live-example></live-example> that the _flying heroes_ You can confirm in the <live-example></live-example> that the _flying heroes_
display updates as you add heroes, even when you mutate the `heroes` #{_array}. display updates as you add heroes, even when you mutate the `heroes` #{_array}.
<h3 id='async-pipe'> <h3 id='async-pipe'>
The impure <i> AsyncPipe </i> The impure <i> AsyncPipe </i>
</h3> </h3>
@ -436,6 +464,7 @@ This next example binds an `#{_Observable}` of message strings
(`message#{_dollar}`) to a view with the `async` pipe. (`message#{_dollar}`) to a view with the `async` pipe.
<code-example path="pipes/src/app/hero-async-message.component.ts"> <code-example path="pipes/src/app/hero-async-message.component.ts">
</code-example> </code-example>
@ -457,6 +486,7 @@ In the following code, the pipe only calls the server when the request URL chang
The code<span if-docs="ts"> uses the [Angular http](guide/server-communication) client to retrieve data</span>: The code<span if-docs="ts"> uses the [Angular http](guide/server-communication) client to retrieve data</span>:
<code-example path="pipes/src/app/fetch-json.pipe.ts"> <code-example path="pipes/src/app/fetch-json.pipe.ts">
</code-example> </code-example>
@ -465,12 +495,14 @@ Now demonstrate it in a harness component whose template defines two bindings to
both requesting the heroes from the `heroes.json` file. both requesting the heroes from the `heroes.json` file.
<code-example path="pipes/src/app/hero-list.component.ts"> <code-example path="pipes/src/app/hero-list.component.ts">
</code-example> </code-example>
The component renders as the following: The component renders as the following:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/pipes/hero-list.png' alt="Hero List"> </img> <img src='assets/images/devguide/pipes/hero-list.png' alt="Hero List"> </img>
</figure> </figure>
@ -489,6 +521,7 @@ It displays the same hero data in JSON format by chaining through to the built-i
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
Debugging with the json pipe Debugging with the json pipe
</header> </header>
@ -550,6 +583,7 @@ by offering `filter` and `orderBy` in the first place.
The minification hazard is also compelling, if less obvious. Imagine a sorting pipe applied to a list of heroes. The minification hazard is also compelling, if less obvious. Imagine a sorting pipe applied to a list of heroes.
The list might be sorted by hero `name` and `planet` of origin properties in the following way: The list might be sorted by hero `name` and `planet` of origin properties in the following way:
<code-example language="html"> <code-example language="html">
&lt;!-- NOT REAL CODE! --> &lt;!-- NOT REAL CODE! -->
&lt;div *ngFor="let hero of heroes | orderBy:'name,planet'">&lt;/div> &lt;div *ngFor="let hero of heroes | orderBy:'name,planet'">&lt;/div>

View File

@ -159,6 +159,7 @@ You'll need a `hero` class and some hero data.
Create a new `data-model.ts` file in the `app` directory and copy the content below into it. Create a new `data-model.ts` file in the `app` directory and copy the content below into it.
<code-example path="reactive-forms/src/app/data-model.ts" linenums="false"> <code-example path="reactive-forms/src/app/data-model.ts" linenums="false">
</code-example> </code-example>
@ -175,6 +176,7 @@ Make a new file called
`hero-detail.component.ts` in the `app` directory and import these symbols: `hero-detail.component.ts` in the `app` directory and import these symbols:
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -182,6 +184,7 @@ Make a new file called
Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata: Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata:
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" linenums="false">
</code-example> </code-example>
@ -192,6 +195,7 @@ a `FormControl` instance directly.
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -223,6 +227,7 @@ read the [Form Validation](cookbook/form-validation) cookbook.
Now create the component's template, `src/app/hero-detail.component.html`, with the following markup. Now create the component's template, `src/app/hero-detail.component.html`, with the following markup.
<code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false">
</code-example> </code-example>
@ -259,6 +264,7 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
1. Add `HeroDetailComponent` to the declarations array. 1. Add `HeroDetailComponent` to the declarations array.
<code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false"> <code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -270,6 +276,7 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
## Display the _HeroDetailComponent_ ## Display the _HeroDetailComponent_
Revise the `AppComponent` template so it displays the `HeroDetailComponent`. Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
<code-example path="reactive-forms/src/app/app.component.1.ts" linenums="false"> <code-example path="reactive-forms/src/app/app.component.1.ts" linenums="false">
</code-example> </code-example>
@ -304,12 +311,14 @@ You used bootstrap CSS classes in the template HTML of both the `AppComponent` a
Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`: Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
<code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false"> <code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false">
</code-example> </code-example>
Now that everything is wired up, the browser should display something like this: Now that everything is wired up, the browser should display something like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -324,6 +333,7 @@ This is simple to do. To add a `FormGroup`, add it to the imports section
of `hero-detail.component.ts`: of `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -331,6 +341,7 @@ of `hero-detail.component.ts`:
In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows: In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" linenums="false">
</code-example> </code-example>
@ -339,6 +350,7 @@ Now that you've made changes in the class, they need to be reflected in the
template. Update `hero-detail.component.html` by replacing it with the following. template. Update `hero-detail.component.html` by replacing it with the following.
<code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false">
</code-example> </code-example>
@ -390,6 +402,7 @@ To see the form model, add the following line after the
closing `form` tag in the `hero-detail.component.html`: closing `form` tag in the `hero-detail.component.html`:
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example> </code-example>
@ -397,6 +410,7 @@ closing `form` tag in the `hero-detail.component.html`:
The `heroForm.value` returns the _form model_. The `heroForm.value` returns the _form model_.
Piping it through the `JsonPipe` renders the model as JSON in the browser: Piping it through the `JsonPipe` renders the model as JSON in the browser:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -421,6 +435,7 @@ clutter by handling details of control creation for you.
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`: To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -435,6 +450,7 @@ by following this plan:
The revised `HeroDetailComponent` looks like this: The revised `HeroDetailComponent` looks like this:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false">
</code-example> </code-example>
@ -454,6 +470,7 @@ demonstrates the simplicity of using `Validators.required` in reactive forms.
First, import the `Validators` symbol. First, import the `Validators` symbol.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -464,6 +481,7 @@ The first item is the initial value for `name`;
the second is the required validator, `Validators.required`. the second is the required validator, `Validators.required`.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" linenums="false">
</code-example> </code-example>
@ -480,12 +498,14 @@ Configuring validation is harder in template-driven forms where you must wrap va
Update the diagnostic message at the bottom of the template to display the form's validity status. Update the diagnostic message at the bottom of the template to display the form's validity status.
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example> </code-example>
The browser displays the following: The browser displays the following:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -506,6 +526,7 @@ A hero has an address, a super power and sometimes a sidekick too.
The address has a state property. The user will select a state with a `<select>` box and you'll populate The address has a state property. The user will select a state with a `<select>` box and you'll populate
the `<option>` elements with states. So import `states` from `data-model.ts`. the `<option>` elements with states. So import `states` from `data-model.ts`.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -513,6 +534,7 @@ the `<option>` elements with states. So import `states` from `data-model.ts`.
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows. Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false">
</code-example> </code-example>
@ -521,6 +543,7 @@ Then add corresponding markup in `hero-detail.component.html`
within the `form` element. within the `form` element.
<code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false">
</code-example> </code-example>
@ -572,6 +595,7 @@ Let that be the parent `FormGroup`.
Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls; Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls;
assign the result to a new `address` property of the parent `FormGroup`. assign the result to a new `address` property of the parent `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" linenums="false">
</code-example> </code-example>
@ -587,6 +611,7 @@ To make this change visually obvious, slip in an `<h4>` header near the top with
The new _address_ HTML looks like this: The new _address_ HTML looks like this:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false">
</code-example> </code-example>
@ -594,6 +619,7 @@ The new _address_ HTML looks like this:
After these changes, the JSON output in the browser shows the revised _form model_ After these changes, the JSON output in the browser shows the revised _form model_
with the nested address `FormGroup`: with the nested address `FormGroup`:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -614,6 +640,7 @@ page by adding the following to the template,
immediately after the `{{form.value | json}}` interpolation as follows: immediately after the `{{form.value | json}}` interpolation as follows:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" linenums="false">
</code-example> </code-example>
@ -621,36 +648,44 @@ immediately after the `{{form.value | json}}` interpolation as follows:
To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control. To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control.
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false">
</code-example> </code-example>
You can use this technique to display _any_ property of a `FormControl` You can use this technique to display _any_ property of a `FormControl`
such as one of the following: such as one of the following:
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
</style> </style>
<table width="100%"> <table width="100%">
<col width="10%"> <col width="10%">
</col> </col>
<col width="90%"> <col width="90%">
</col> </col>
<tr> <tr>
<th> <th>
Property Property
</th> </th>
<th> <th>
Description Description
</th> </th>
@ -659,13 +694,16 @@ such as one of the following:
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.value</code> <code>myControl.value</code>
</td> </td>
<td> <td>
the value of a `FormControl`. the value of a `FormControl`.
</td> </td>
@ -674,13 +712,16 @@ such as one of the following:
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.status</code> <code>myControl.status</code>
</td> </td>
<td> <td>
the validity of a `FormControl`. Possible values: `VALID`, the validity of a `FormControl`. Possible values: `VALID`,
`INVALID`, `PENDING`, or `DISABLED`. `INVALID`, `PENDING`, or `DISABLED`.
@ -690,13 +731,16 @@ such as one of the following:
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.pristine</code> <code>myControl.pristine</code>
</td> </td>
<td> <td>
`true` if the user has _not_ changed the value in the UI. `true` if the user has _not_ changed the value in the UI.
Its opposite is `myControl.dirty`. Its opposite is `myControl.dirty`.
@ -706,13 +750,16 @@ such as one of the following:
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.untouched</code> <code>myControl.untouched</code>
</td> </td>
<td> <td>
`true` if the control user has not yet entered the HTML control `true` if the control user has not yet entered the HTML control
and triggered its blur event. Its opposite is `myControl.touched`. and triggered its blur event. Its opposite is `myControl.touched`.
@ -764,6 +811,7 @@ In this `HeroDetailComponent`, the two models are quite close.
Recall the definition of `Hero` in `data-model.ts`: Recall the definition of `Hero` in `data-model.ts`:
<code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false"> <code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false">
</code-example> </code-example>
@ -771,6 +819,7 @@ Recall the definition of `Hero` in `data-model.ts`:
Here, again, is the component's `FormGroup` definition. Here, again, is the component's `FormGroup` definition.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" linenums="false">
</code-example> </code-example>
@ -786,12 +835,14 @@ Nonetheless, the two models are pretty close in shape and you'll see in a moment
to the _form model_ with the `patchValue` and `setValue` methods. to the _form model_ with the `patchValue` and `setValue` methods.
Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows: Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example> </code-example>
Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes: Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" linenums="false">
</code-example> </code-example>
@ -810,6 +861,7 @@ With **`setValue`**, you assign _every_ form control value _at once_
by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`. by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false">
</code-example> </code-example>
@ -830,6 +882,7 @@ because its shape is similar to the component's `FormGroup` structure.
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all. You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all.
This explains the conditional setting of the `address` property in the data object argument: This explains the conditional setting of the `address` property in the data object argument:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" linenums="false">
</code-example> </code-example>
@ -840,6 +893,7 @@ by supplying an object of key/value pairs for just the controls of interest.
This example sets only the form's `name` control. This example sets only the form's `name` control.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false">
</code-example> </code-example>
@ -859,6 +913,7 @@ When the user clicks on a hero, the list component passes the selected hero into
by binding to its `hero` input property. by binding to its `hero` input property.
<code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false"> <code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false">
</code-example> </code-example>
@ -872,12 +927,14 @@ as the following steps demonstrate.
First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`. First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" linenums="false">
</code-example> </code-example>
Add the `hero` input property. Add the `hero` input property.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false">
</code-example> </code-example>
@ -885,6 +942,7 @@ Add the `hero` input property.
Add the `ngOnChanges` method to the class as follows: Add the `ngOnChanges` method to the class as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" linenums="false">
</code-example> </code-example>
@ -896,6 +954,7 @@ control values from the previous hero are cleared and
status flags are restored to the _pristine_ state. status flags are restored to the _pristine_ state.
You could call `reset` at the top of `ngOnChanges` like this. You could call `reset` at the top of `ngOnChanges` like this.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false">
</code-example> </code-example>
@ -904,6 +963,7 @@ The `reset` method has an optional `state` value so you can reset the flags _and
Internally, `reset` passes the argument to `setValue`. Internally, `reset` passes the argument to `setValue`.
A little refactoring and `ngOnChanges` becomes this: A little refactoring and `ngOnChanges` becomes this:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" linenums="false">
</code-example> </code-example>
@ -916,6 +976,7 @@ A little refactoring and `ngOnChanges` becomes this:
The `HeroDetailComponent` is a nested sub-component of the `HeroListComponent` in a _master/detail_ view. The `HeroDetailComponent` is a nested sub-component of the `HeroListComponent` in a _master/detail_ view.
Together they look a bit like this: Together they look a bit like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -962,6 +1023,7 @@ An Angular `FormArray` can display an array of _address_ `FormGroups`.
To get access to the `FormArray` class, import it into `hero-detail.component.ts`: To get access to the `FormArray` class, import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -977,6 +1039,7 @@ let the user add or modify addresses (removing addresses is your homework).
Youll need to redefine the form model in the `HeroDetailComponent` constructor, Youll need to redefine the form model in the `HeroDetailComponent` constructor,
which currently only displays the first hero address in an _address_ `FormGroup`. which currently only displays the first hero address in an _address_ `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example> </code-example>
@ -987,6 +1050,7 @@ From the user's point of view, heroes don't have _addresses_.
_Addresses_ are for mere mortals. Heroes have _secret lairs_! _Addresses_ are for mere mortals. Heroes have _secret lairs_!
Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition: Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" linenums="false">
</code-example> </code-example>
@ -1017,6 +1081,7 @@ the parent `HeroListComponent` sets the `HeroListComponent.hero` input property
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`, The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
initialized by an array of hero address `FormGroups`. initialized by an array of hero address `FormGroups`.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" linenums="false">
</code-example> </code-example>
@ -1032,6 +1097,7 @@ The `HeroDetailComponent` should be able to display, add, and remove items from
Use the `FormGroup.get` method to acquire a reference to that `FormArray`. Use the `FormGroup.get` method to acquire a reference to that `FormArray`.
Wrap the expression in a `secretLairs` convenience property for clarity and re-use. Wrap the expression in a `secretLairs` convenience property for clarity and re-use.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" linenums="false">
</code-example> </code-example>
@ -1058,12 +1124,14 @@ You'll re-use that index to compose a unique label for each address.
Here's the skeleton for the _secret lairs_ section of the HTML template: Here's the skeleton for the _secret lairs_ section of the HTML template:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false">
</code-example> </code-example>
Here's the complete template for the _secret lairs_ section: Here's the complete template for the _secret lairs_ section:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array">
</code-example> </code-example>
@ -1072,6 +1140,7 @@ Here's the complete template for the _secret lairs_ section:
Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it. Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false">
</code-example> </code-example>
@ -1079,6 +1148,7 @@ Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a ne
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method. Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method.
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false">
</code-example> </code-example>
@ -1102,6 +1172,7 @@ You do not want to save changes when the user clicks the _Add a Secret Lair_ but
Back in the browser, select the hero named "Magneta". 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. "Magneta" doesn't have an address, as you can see in the diagnostic JSON at the bottom of the form.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -1131,12 +1202,14 @@ You don't need to know much about RxJS `Observable` to monitor form control valu
Add the following method to log changes to the value of the _name_ `FormControl`. Add the following method to log changes to the value of the _name_ `FormControl`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false">
</code-example> </code-example>
Call it in the constructor, after creating the form. Call it in the constructor, after creating the form.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -1144,6 +1217,7 @@ Call it in the constructor, after creating the form.
The `logNameChange` method pushes name-change values into a `nameChangeLog` array. The `logNameChange` method pushes name-change values into a `nameChangeLog` array.
Display that array at the bottom of the component template with this `*ngFor` binding: Display that array at the bottom of the component template with this `*ngFor` binding:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" linenums="false">
</code-example> </code-example>
@ -1167,6 +1241,7 @@ In a real app, you'd probably save those hero changes.
In a real app, you'd also be able to revert unsaved changes and resume editing. 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: After you implement both features in this section, the form will look like this:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -1176,6 +1251,7 @@ In this sample application, when the user submits the form,
the `HeroDetailComponent` will pass an instance of the hero _data model_ the `HeroDetailComponent` will pass an instance of the hero _data model_
to a save method on the injected `HeroService`. to a save method on the injected `HeroService`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" linenums="false">
</code-example> </code-example>
@ -1185,6 +1261,7 @@ So you create a new `hero` from a combination of original hero values (the `hero
and deep copies of the changed form model values, using the `prepareSaveHero` helper. and deep copies of the changed form model values, using the `prepareSaveHero` helper.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false">
</code-example> </code-example>
@ -1210,6 +1287,7 @@ The user cancels changes and reverts the form to the original state by pressing
Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_. Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false">
</code-example> </code-example>
@ -1217,6 +1295,7 @@ Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _fo
### Buttons ### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template: Add the "Save" and "Revert" buttons near the top of the component's template:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" linenums="false">
</code-example> </code-example>
@ -1249,43 +1328,52 @@ This page covered:
The key files of the final version are as follows: The key files of the final version are as follows:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts"> <code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html"> <code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html"> <code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts"> <code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts"> <code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts"> <code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts">
</code-pane> </code-pane>

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ For more information about the attacks and mitigations described below, see [OWA
You can run the <live-example></live-example> in Plunker and download the code from there. You can run the <live-example></live-example> in Plunker and download the code from there.
<h2 id='report-issues'> <h2 id='report-issues'>
Reporting vulnerabilities Reporting vulnerabilities
</h2> </h2>
@ -25,6 +26,7 @@ For more information about how Google handles security issues, see [Google's sec
philosophy](https://www.google.com/about/appsecurity/). philosophy](https://www.google.com/about/appsecurity/).
<h2 id='best-practices'> <h2 id='best-practices'>
Best practices Best practices
</h2> </h2>
@ -43,6 +45,7 @@ community and make a pull request.
For more information, see the [Trusting safe values](guide/security#bypass-security-apis) section of this page. For more information, see the [Trusting safe values](guide/security#bypass-security-apis) section of this page.
<h2 id='xss'> <h2 id='xss'>
Preventing cross-site scripting (XSS) Preventing cross-site scripting (XSS)
</h2> </h2>
@ -94,6 +97,7 @@ The following template binds the value of `htmlSnippet`, once by interpolating i
content, and once by binding it to the `innerHTML` property of an element: content, and once by binding it to the `innerHTML` property of an element:
<code-example path="security/src/app/inner-html-binding.component.html"> <code-example path="security/src/app/inner-html-binding.component.html">
</code-example> </code-example>
@ -106,6 +110,7 @@ a value that an attacker might control into `innerHTML` normally causes an XSS
vulnerability. For example, code contained in a `<script>` tag is executed: vulnerability. For example, code contained in a `<script>` tag is executed:
<code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" title="src/app/inner-html-binding.component.ts (class)" region="class"> <code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" title="src/app/inner-html-binding.component.ts (class)" region="class">
</code-example> </code-example>
@ -114,6 +119,7 @@ vulnerability. For example, code contained in a `<script>` tag is executed:
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>` Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>`
tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element. tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'> </img> <img src='assets/images/devguide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'> </img>
</figure> </figure>
@ -154,6 +160,7 @@ carries a high risk of introducing template-injection vulnerabilities.
<h2 id='bypass-security-apis'> <h2 id='bypass-security-apis'>
Trusting safe values Trusting safe values
</h2> </h2>
@ -179,6 +186,7 @@ your intended use of the value. Imagine that the following template needs to bin
`javascript:alert(...)` call: `javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL"> <code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL">
</code-example> </code-example>
@ -188,11 +196,13 @@ in development mode, logs this action to the console. To prevent
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call: this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url"> <code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'> </img> <img src='assets/images/devguide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'> </img>
</figure> </figure>
@ -205,12 +215,14 @@ could execute. So call a method on the controller to construct a trusted video U
Angular to allow binding into `<iframe src>`: Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe"> <code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example> </code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"> <code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example> </code-example>
@ -218,6 +230,7 @@ Angular to allow binding into `<iframe src>`:
<h2 id='http'> <h2 id='http'>
HTTP-level vulnerabilities HTTP-level vulnerabilities
</h2> </h2>
@ -226,6 +239,7 @@ Angular has built-in support to help prevent two common HTTP vulnerabilities, cr
forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily
on the server side, but Angular provides helpers to make integration on the client side easier. on the server side, but Angular provides helpers to make integration on the client side easier.
<h3 id='xsrf'> <h3 id='xsrf'>
Cross-site request forgery Cross-site request forgery
</h3> </h3>
@ -271,12 +285,14 @@ cryptographically secure random number generator, and expire in a day or two.
Your server may use a different cookie or header name for this purpose. Your server may use a different cookie or header name for this purpose.
An Angular application can customize cookie and header names by providing its own `CookieXSRFStrategy` values. An Angular application can customize cookie and header names by providing its own `CookieXSRFStrategy` values.
<code-example language="typescript"> <code-example language="typescript">
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name') } { provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name') }
</code-example> </code-example>
Or you can implement and provide an entirely custom `XSRFStrategy`: Or you can implement and provide an entirely custom `XSRFStrategy`:
<code-example language="typescript"> <code-example language="typescript">
{ provide: XSRFStrategy, useClass: MyXSRFStrategy } { provide: XSRFStrategy, useClass: MyXSRFStrategy }
@ -291,6 +307,7 @@ The Stanford University paper
See also Dave Smith's easy-to-understand See also Dave Smith's easy-to-understand
<a href="https://www.youtube.com/watch?v=9inczw6qtpY" target="_blank" title="Cross Site Request Funkery Securing Your Angular Apps From Evil Doers">talk on XSRF at AngularConnect 2016</a>. <a href="https://www.youtube.com/watch?v=9inczw6qtpY" target="_blank" title="Cross Site Request Funkery Securing Your Angular Apps From Evil Doers">talk on XSRF at AngularConnect 2016</a>.
<h3 id='xssi'> <h3 id='xssi'>
Cross-site script inclusion (XSSI) Cross-site script inclusion (XSSI)
</h3> </h3>
@ -310,6 +327,7 @@ For more information, see the XSSI section of this [Google web security blog
post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html). post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html).
<h2 id='code-review'> <h2 id='code-review'>
Auditing Angular applications Auditing Angular applications
</h2> </h2>

View File

@ -75,6 +75,7 @@ This page describes server communication with the help of the following demos:
- [More fun with Observables](guide/server-communication#more-observables). - [More fun with Observables](guide/server-communication#more-observables).
The root `AppComponent` orchestrates these demos: The root `AppComponent` orchestrates these demos:
<code-example path="server-communication/src/app/app.component.ts"> <code-example path="server-communication/src/app/app.component.ts">
</code-example> </code-example>
@ -99,6 +100,7 @@ Read about providers in the [Dependency Injection](guide/dependency-injection) p
Register providers by importing other NgModules to the root NgModule in `app.module.ts`. Register providers by importing other NgModules to the root NgModule in `app.module.ts`.
<code-example path="server-communication/src/app/app.module.1.ts" linenums="false"> <code-example path="server-communication/src/app/app.module.1.ts" linenums="false">
</code-example> </code-example>
@ -126,12 +128,14 @@ This version gets some heroes from the server, displays them in a list, lets the
The app uses the !{_Angular_Http} client to communicate via **XMLHttpRequest (XHR)**. The app uses the !{_Angular_Http} client to communicate via **XMLHttpRequest (XHR)**.
It works like this: It works like this:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
This demo has a single component, the `HeroListComponent`. Here's its template: This demo has a single component, the `HeroListComponent`. Here's its template:
<code-example path="server-communication/src/app/toh/hero-list.component.html"> <code-example path="server-communication/src/app/toh/hero-list.component.html">
</code-example> </code-example>
@ -154,6 +158,7 @@ Below the button is an area for an error message.
### The *HeroListComponent* class ### The *HeroListComponent* class
Here's the component class: Here's the component class:
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component">
</code-example> </code-example>
@ -197,12 +202,14 @@ With a basic understanding of the component, you're ready to look inside the `He
In many of the previous samples the app faked the interaction with the server by In many of the previous samples the app faked the interaction with the server by
returning mock heroes in a service like this one: returning mock heroes in a service like this one:
<code-example path="toh-4/src/app/hero.service.ts" region="just-get-heroes" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="just-get-heroes" linenums="false">
</code-example> </code-example>
You can revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service: You can revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="v1"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="v1">
</code-example> </code-example>
@ -210,12 +217,14 @@ You can revise that `HeroService` to get the heroes from the server using the !{
Notice that the !{_Angular_Http} client service is Notice that the !{_Angular_Http} client service is
[injected](guide/dependency-injection) into the `HeroService` constructor. [injected](guide/dependency-injection) into the `HeroService` constructor.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor">
</code-example> </code-example>
Look closely at how to call `!{_priv}http.get`: Look closely at how to call `!{_priv}http.get`:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="http-get" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="http-get" linenums="false">
</code-example> </code-example>
@ -229,6 +238,7 @@ The server returns heroes once you've set up the [in-memory web api](guide/serve
described in the appendix below. described in the appendix below.
Alternatively, you can temporarily target a JSON file by changing the endpoint URL: Alternatively, you can temporarily target a JSON file by changing the endpoint URL:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example> </code-example>
@ -244,6 +254,7 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U
## Process the response object ## Process the response object
Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes: Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false">
</code-example> </code-example>
@ -314,6 +325,7 @@ but only if it says something that the user can understand and act upon.
This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error. This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false">
</code-example> </code-example>
@ -329,6 +341,7 @@ logs it to the console, and returns the message in a new, failed Observable via
{@a hero-list-component} {@a hero-list-component}
<h3> <h3>
<b> HeroListComponent </b> error handling <b> HeroListComponent </b> error handling
</h3> </h3>
@ -339,6 +352,7 @@ the `subscribe` function has a second function parameter to handle the error mes
It sets an `errorMessage` variable that's bound conditionally in the `HeroListComponent` template. It sets an `errorMessage` variable that's bound conditionally in the `HeroListComponent` template.
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false">
</code-example> </code-example>
@ -365,6 +379,7 @@ You'll write a method for the `HeroListComponent` to call, a `create()` method,
just the name of a new hero and returns an `Observable` of `Hero`. It begins like this: just the name of a new hero and returns an `Observable` of `Hero`. It begins like this:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false">
</code-example> </code-example>
@ -378,6 +393,7 @@ It expects the new hero data to arrive in the body of the request,
structured like a `Hero` entity but without the `id` property. structured like a `Hero` entity but without the `id` property.
The body of the request should look like this: The body of the request should look like this:
<code-example format="." language="javascript"> <code-example format="." language="javascript">
{ "name": "Windstorm" } { "name": "Windstorm" }
</code-example> </code-example>
@ -389,6 +405,7 @@ with its own `data` property.
Now that you know how the API works, implement `create()` as follows: Now that you know how the API works, implement `create()` as follows:
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create"> <code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
</code-example> </code-example>
@ -410,11 +427,13 @@ from the response.
Back in the `HeroListComponent`, its `addHero()` method subscribes to the Observable returned by the service's `create()` method. Back in the `HeroListComponent`, its `addHero()` method subscribes to the Observable returned by the service's `create()` method.
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user. When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="addHero" linenums="false"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="addHero" linenums="false">
</code-example> </code-example>
<h2 id='cors'> <h2 id='cors'>
Cross-Origin Requests: Wikipedia example Cross-Origin Requests: Wikipedia example
</h2> </h2>
@ -453,6 +472,7 @@ This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-
Here is a simple search that shows suggestions from Wikipedia as the user Here is a simple search that shows suggestions from Wikipedia as the user
types in a text box: types in a text box:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -465,6 +485,7 @@ All other HTTP methods throw an error because `JSONP` is a read-only facility.
As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`. As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts">
</code-example> </code-example>
@ -489,18 +510,21 @@ All of this happens under the hood.
If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this: If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this:
<code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false">
</code-example> </code-example>
In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper: In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper:
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false">
</code-example> </code-example>
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object. This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false">
</code-example> </code-example>
@ -512,6 +536,7 @@ to the server.
Now that you have a service that can query the Wikipedia API, Now that you have a service that can query the Wikipedia API,
turn your attention to the component (template and class) that takes user input and displays search results. turn your attention to the component (template and class) that takes user input and displays search results.
<code-example path="server-communication/src/app/wiki/wiki.component.ts"> <code-example path="server-communication/src/app/wiki/wiki.component.ts">
</code-example> </code-example>
@ -547,6 +572,7 @@ It is inefficient and potentially expensive on mobile devices with limited data
Presently, the code calls the server after every keystroke. Presently, the code calls the server after every keystroke.
It should only make requests when the user *stops typing*. It should only make requests when the user *stops typing*.
Here's how it will work after refactoring: Here's how it will work after refactoring:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"> </img> <img src='assets/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"> </img>
</figure> </figure>
@ -580,13 +606,16 @@ with the help of some nifty Observable operators.
Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`: Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`:
<code-tabs> <code-tabs>
<code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts"> <code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts"> <code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts">
</code-pane> </code-pane>
@ -608,6 +637,7 @@ The `WikiComponent` passes a new search term directly to the `WikipediaService`
The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_ The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_
with the help of a `Subject`, which you import from RxJS: with the help of a `Subject`, which you import from RxJS:
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="import-subject" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="import-subject" linenums="false">
</code-example> </code-example>
@ -616,6 +646,7 @@ The component creates a `searchTermStream` as a `Subject` of type `string`.
The `search()` method adds each new search box value to that stream via the subject's `next()` method. The `search()` method adds each new search box value to that stream via the subject's `next()` method.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false">
</code-example> </code-example>
@ -628,6 +659,7 @@ The `search()` method adds each new search box value to that stream via the subj
The `WikiSmartComponent` listens to the *stream of search terms* and The `WikiSmartComponent` listens to the *stream of search terms* and
processes that stream _before_ calling the service. processes that stream _before_ calling the service.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="observable-operators" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="observable-operators" linenums="false">
</code-example> </code-example>
@ -686,12 +718,14 @@ This sample creates a class that sets the default `Content-Type` header to JSON.
It exports a constant with the necessary `RequestOptions` provider to simplify registration in `AppModule`. It exports a constant with the necessary `RequestOptions` provider to simplify registration in `AppModule`.
<code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false"> <code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false">
</code-example> </code-example>
Then it registers the provider in the root `AppModule`. Then it registers the provider in the root `AppModule`.
<code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false"> <code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false">
</code-example> </code-example>
@ -707,6 +741,7 @@ Remember to include this provider during setup when unit testing the app's HTTP
After this change, the `header` option setting in `HeroService.create()` is no longer necessary, After this change, the `header` option setting in `HeroService.create()` is no longer necessary,
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create"> <code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
</code-example> </code-example>
@ -737,6 +772,7 @@ posed by top-level JSON arrays.
You'd set the endpoint to the JSON file like this: You'd set the endpoint to the JSON file like this:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example> </code-example>
@ -767,12 +803,14 @@ are !{_array}s of objects in those collections.
Here's the class for this sample, based on the JSON data: Here's the class for this sample, based on the JSON data:
<code-example path="server-communication/src/app/hero-data.ts" linenums="false"> <code-example path="server-communication/src/app/hero-data.ts" linenums="false">
</code-example> </code-example>
Ensure that the `HeroService` endpoint refers to the web API: Ensure that the `HeroService` endpoint refers to the web API:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint" linenums="false">
</code-example> </code-example>
@ -782,6 +820,7 @@ Finally, redirect client HTTP requests to the in-memory web API by
adding the `InMemoryWebApiModule` to the `AppModule.imports` list. adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
At the same time, call its `forRoot()` configuration method with the `HeroData` class. At the same time, call its `forRoot()` configuration method with the `HeroData` class.
<code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false"> <code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false">
</code-example> </code-example>
@ -805,6 +844,7 @@ while setting the metadata for the root `AppModule`. Don't call it again.
Here is the final, revised version of <span ngio-ex>src/app/app.module.ts</span>, demonstrating these steps. Here is the final, revised version of <span ngio-ex>src/app/app.module.ts</span>, demonstrating these steps.
<code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)"> <code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)">
</code-example> </code-example>

View File

@ -11,10 +11,13 @@ Setting the document or window title using the Title service.
Our app should be able to make the browser title bar say whatever we want it to say. Our app should be able to make the browser title bar say whatever we want it to say.
This cookbook explains how to do it.**See the <live-example name="cb-set-document-title"></live-example>**. This cookbook explains how to do it.**See the <live-example name="cb-set-document-title"></live-example>**.
<table> <table>
<tr> <tr>
<td> <td>
To see the browser title bar change in the live example, To see the browser title bar change in the live example,
open it again in the Plunker editor by clicking the icon in the upper right, open it again in the Plunker editor by clicking the icon in the upper right,
@ -22,6 +25,7 @@ This cookbook explains how to do it.**See the <live-example name="cb-set-documen
</td> </td>
<td> <td>
<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> <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> </td>
@ -35,6 +39,7 @@ This cookbook explains how to do it.**See the <live-example name="cb-set-documen
## The problem with *&lt;title&gt;* ## The problem with *&lt;title&gt;*
The obvious approach is to bind a property of the component to the HTML `<title>` like this: The obvious approach is to bind a property of the component to the HTML `<title>` like this:
<code-example format=''> <code-example format=''>
&lt;title&gt;{{This_Does_Not_Work}}&lt;/title&gt; &lt;title&gt;{{This_Does_Not_Work}}&lt;/title&gt;
</code-example> </code-example>
@ -67,11 +72,13 @@ for getting and setting the current HTML document title:
Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it: Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
<code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false"> <code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false">
</code-example> </code-example>
We bind that method to three anchor tags and, voilà! We bind that method to three anchor tags and, voilà!
<figure class='image-display'> <figure class='image-display'>
<img src="assets/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> </figure>
@ -79,18 +86,22 @@ We bind that method to three anchor tags and, voilà!
Here's the complete solution Here's the complete solution
<code-tabs> <code-tabs>
<code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts"> <code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts">
</code-pane> </code-pane>

View File

@ -15,30 +15,37 @@ Files _outside_ those folders condition the development environment.
They rarely change and you may never view or modify them. They rarely change and you may never view or modify them.
If you do, this page can help you understand their purpose. If you do, this page can help you understand their purpose.
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
</style> </style>
<table width="100%"> <table width="100%">
<col width="10%"> <col width="10%">
</col> </col>
<col width="90%"> <col width="90%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
@ -47,13 +54,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>src/app/</code> <code>src/app/</code>
</td> </td>
<td> <td>
Angular application files go here. Angular application files go here.
@ -70,13 +80,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>e2e/</code> <code>e2e/</code>
</td> </td>
<td> <td>
_End-to-end_ (e2e) tests of the application, _End-to-end_ (e2e) tests of the application,
written in Jasmine and run by the written in Jasmine and run by the
@ -90,13 +103,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>node_modules/</code> <code>node_modules/</code>
</td> </td>
<td> <td>
The _npm_ packages installed with the `npm install` command. The _npm_ packages installed with the `npm install` command.
</td> </td>
@ -105,8 +121,10 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code> .editorconfig<br> <code> .editorconfig<br>
.git/<br> .git/<br>
@ -115,6 +133,7 @@ If you do, this page can help you understand their purpose.
</td> </td>
<td> <td>
Tooling configuration files and folders. Tooling configuration files and folders.
Ignore them until you have a compelling reason to do otherwise. Ignore them until you have a compelling reason to do otherwise.
@ -124,13 +143,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>CHANGELOG.md</code> <code>CHANGELOG.md</code>
</td> </td>
<td> <td>
The history of changes to the _QuickStart_ repository. The history of changes to the _QuickStart_ repository.
Delete or ignore. Delete or ignore.
@ -140,13 +162,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>favicon.ico</code> <code>favicon.ico</code>
</td> </td>
<td> <td>
The application icon that appears in the browser tab. The application icon that appears in the browser tab.
</td> </td>
@ -155,13 +180,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>index.html</code> <code>index.html</code>
</td> </td>
<td> <td>
The application host page. The application host page.
It loads a few essential scripts in a prescribed order. It loads a few essential scripts in a prescribed order.
@ -175,13 +203,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma.conf.js</code> <code>karma.conf.js</code>
</td> </td>
<td> <td>
Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a> Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
test runner described in the [Testing](guide/testing) guide. test runner described in the [Testing](guide/testing) guide.
@ -191,13 +222,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma-test-shim.js</code> <code>karma-test-shim.js</code>
</td> </td>
<td> <td>
Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a> Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
with SystemJS as described in the [Testing](guide/testing) guide. with SystemJS as described in the [Testing](guide/testing) guide.
@ -207,13 +241,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>non-essential-files.txt</code> <code>non-essential-files.txt</code>
</td> </td>
<td> <td>
A list of files that you can delete if you want to purge your setup of the A list of files that you can delete if you want to purge your setup of the
original QuickStart Seed testing and git maintainence artifacts. original QuickStart Seed testing and git maintainence artifacts.
@ -226,13 +263,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>LICENSE</code> <code>LICENSE</code>
</td> </td>
<td> <td>
The open source MIT license to use this setup code in your application. The open source MIT license to use this setup code in your application.
</td> </td>
@ -241,13 +281,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>package.json</code> <code>package.json</code>
</td> </td>
<td> <td>
Identifies `npm `package dependencies for the project. Identifies `npm `package dependencies for the project.
@ -261,13 +304,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>protractor.config.js</code> <code>protractor.config.js</code>
</td> </td>
<td> <td>
Configuration for the Configuration for the
<a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a> <a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a>
@ -278,13 +324,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>README.md</code> <code>README.md</code>
</td> </td>
<td> <td>
Instruction for using this git repository in your project. Instruction for using this git repository in your project.
Worth reading before deleting. Worth reading before deleting.
@ -294,13 +343,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>styles.css</code> <code>styles.css</code>
</td> </td>
<td> <td>
Global styles for the application. Initialized with an `<h1>` style for the QuickStart demo. Global styles for the application. Initialized with an `<h1>` style for the QuickStart demo.
@ -310,16 +362,20 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>systemjs<br>.config.js</code> <code>systemjs<br>.config.js</code>
</td> </td>
<td> <td>
Tells the **SystemJS** module loader where to find modules Tells the **SystemJS** module loader where to find modules
referenced in JavaScript `import` statements. For example: referenced in JavaScript `import` statements. For example:
<code-example language="ts"> <code-example language="ts">
import { Component } from '@angular/core; import { Component } from '@angular/core;
</code-example> </code-example>
@ -331,13 +387,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>systemjs<br>.config.extras.js</code> <code>systemjs<br>.config.extras.js</code>
</td> </td>
<td> <td>
Optional extra SystemJS configuration. Optional extra SystemJS configuration.
A way to add SystemJS mappings, such as for appliation _barrels_, A way to add SystemJS mappings, such as for appliation _barrels_,
@ -348,13 +407,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.json</code> <code>tsconfig.json</code>
</td> </td>
<td> <td>
Tells the TypeScript compiler how to transpile TypeScript source files Tells the TypeScript compiler how to transpile TypeScript source files
into JavaScript files that run in all modern browsers. into JavaScript files that run in all modern browsers.
@ -364,13 +426,16 @@ If you do, this page can help you understand their purpose.
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tslint.json</code> <code>tslint.json</code>
</td> </td>
<td> <td>
The `npm` installed TypeScript linter inspects your TypeScript code The `npm` installed TypeScript linter inspects your TypeScript code
and complains when you violate one of its rules. and complains when you violate one of its rules.

View File

@ -32,6 +32,7 @@ Then ...
Perform the _clone-to-launch_ steps with these terminal commands. Perform the _clone-to-launch_ steps with these terminal commands.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
git clone .git quickstart git clone .git quickstart
cd quickstart cd quickstart
@ -56,6 +57,7 @@ Perform the _clone-to-launch_ steps with these terminal commands.
<a href="!{_qsRepoZip}" title="Download the QuickStart seed repository">Download the QuickStart seed</a> <a href="!{_qsRepoZip}" title="Download the QuickStart seed repository">Download the QuickStart seed</a>
and unzip it into your project folder. Then perform the remaining steps with these terminal commands. and unzip it into your project folder. Then perform the remaining steps with these terminal commands.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
cd quickstart cd quickstart
@ -92,6 +94,7 @@ Do this only in the beginning to avoid accidentally deleting your own tests and
Open a terminal window in the project folder and enter the following commands for your environment: Open a terminal window in the project folder and enter the following commands for your environment:
### OS/X (bash) ### OS/X (bash)
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
xargs rm -rf &lt; non-essential-files.osx.txt xargs rm -rf &lt; non-essential-files.osx.txt
rm src/app/*.spec*.ts rm src/app/*.spec*.ts
@ -100,6 +103,7 @@ Open a terminal window in the project folder and enter the following commands fo
</code-example> </code-example>
### Windows ### Windows
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
for /f %i in (non-essential-files.txt) do del %i /F /S /Q for /f %i in (non-essential-files.txt) do del %i /F /S /Q
rd .git /s /q rd .git /s /q
@ -123,17 +127,22 @@ most of which you can [learn about later](guide/setup-systemjs-anatomy).
{@a app-files} {@a app-files}
Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder. Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
@ -142,6 +151,7 @@ Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
@ -154,18 +164,22 @@ Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="setup/src/main.ts"> <code-pane title="src/main.ts" path="setup/src/main.ts">
</code-pane> </code-pane>
@ -185,30 +199,37 @@ unless told to do otherwise.
The following are all in `src/` The following are all in `src/`
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
</style> </style>
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
@ -217,13 +238,16 @@ The following are all in `src/`
</tr> </tr>
<tr> <tr>
<td> <td>
<ngio-ex>app/app.component.ts</ngio-ex> <ngio-ex>app/app.component.ts</ngio-ex>
</td> </td>
<td> <td>
Defines the same `AppComponent` as the one in the QuickStart !{_playground}. Defines the same `AppComponent` as the one in the QuickStart !{_playground}.
It is the **root** component of what will become a tree of nested components It is the **root** component of what will become a tree of nested components
@ -234,13 +258,16 @@ The following are all in `src/`
</tr> </tr>
<tr if-docs="ts"> <tr if-docs="ts">
<td> <td>
<code>app/app.module.ts</code> <code>app/app.module.ts</code>
</td> </td>
<td> <td>
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application. Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`. Right now it declares only the `AppComponent`.
@ -251,13 +278,16 @@ The following are all in `src/`
</tr> </tr>
<tr> <tr>
<td> <td>
<ngio-ex>main.ts</ngio-ex> <ngio-ex>main.ts</ngio-ex>
</td> </td>
<td> <td>
Compiles the application with the [JIT compiler](glossary) and Compiles the application with the [JIT compiler](glossary) and
[bootstraps](guide/appmodule) [bootstraps](guide/appmodule)

View File

@ -7,6 +7,7 @@ Angular has a powerful template engine that lets us easily manipulate the DOM st
@description @description
<style> <style>
h4 {font-size: 17px !important; text-transform: none !important;} h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; } .syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -49,6 +50,7 @@ Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example. An asterisk (*) precedes the directive attribute name as in this example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example> </code-example>
@ -68,6 +70,7 @@ described in the [_Template Syntax_](guide/template-syntax) guide and seen in sa
Here's an example of them in a template: Here's an example of them in a template:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in">
</code-example> </code-example>
@ -79,6 +82,7 @@ and how to [write your own](guide/structural-directives#unless) structural direc
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
Directive spelling Directive spelling
</header> </header>
@ -128,6 +132,7 @@ You can [only apply one](guide/structural-directives#one-per-element) _structura
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear. It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-true)" region="ngif-true"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-true)" region="ngif-true">
</code-example> </code-example>
@ -135,6 +140,7 @@ It takes a boolean expression and makes an entire chunk of the DOM appear or dis
The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM. The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM.
Confirm that fact using browser developer tools to inspect the DOM. Confirm that fact using browser developer tools to inspect the DOM.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -152,12 +158,14 @@ The component and DOM nodes can be garbage-collected and free up memory.
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`. A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (display-none)" region="display-none"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (display-none)" region="display-none">
</code-example> </code-example>
While invisible, the element remains in the DOM. While invisible, the element remains in the DOM.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -196,6 +204,7 @@ and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists. Here is `*ngIf` displaying the hero's name if `hero` exists.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (asterisk)" region="asterisk"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (asterisk)" region="asterisk">
</code-example> </code-example>
@ -205,6 +214,7 @@ Internally, Angular desugars it in two stages.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this. First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr">
</code-example> </code-example>
@ -212,6 +222,7 @@ First, it translates the `*ngIf="..."` into a template _attribute_, `template="n
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this. Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template">
</code-example> </code-example>
@ -222,6 +233,7 @@ Then it translates the template _attribute_ into a template _element_, wrapped a
None of these forms are actually rendered. None of these forms are actually rendered.
Only the finished product ends up in the DOM. Only the finished product ends up in the DOM.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -242,6 +254,7 @@ template _attribute_ to template _element_.
Here's a full-featured application of `NgFor`, written all three ways: Here's a full-featured application of `NgFor`, written all three ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example> </code-example>
@ -351,6 +364,7 @@ The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`,
Here's an example. Here's an example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example> </code-example>
@ -381,6 +395,7 @@ As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form. can be desugared into the template _attribute_ form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr">
</code-example> </code-example>
@ -388,6 +403,7 @@ can be desugared into the template _attribute_ form.
That, in turn, can be desugared into the `<template>` element form. That, in turn, can be desugared into the `<template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
</code-example> </code-example>
@ -420,12 +436,14 @@ those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!". That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (template-tag)" region="template-tag"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (template-tag)" region="template-tag">
</code-example> </code-example>
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic. Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -445,6 +463,7 @@ There's often a _root_ element that can and should host the structural directive
The list element (`<li>`) is a typical host element of an `NgFor` repeater. The list element (`<li>`) is a typical host element of an `NgFor` repeater.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngfor-li)" region="ngfor-li"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngfor-li)" region="ngfor-li">
</code-example> </code-example>
@ -453,6 +472,7 @@ When there isn't a host element, you can usually wrap the content in a native HT
such as a `<div>`, and attach the directive to that wrapper. such as a `<div>`, and attach the directive to that wrapper.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example> </code-example>
@ -466,6 +486,7 @@ neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout. For example, suppose you have the following paragraph layout.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-span)" region="ngif-span"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-span)" region="ngif-span">
</code-example> </code-example>
@ -473,12 +494,14 @@ For example, suppose you have the following paragraph layout.
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph. You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
<code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span"> <code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span">
</code-example> </code-example>
The constructed paragraph renders strangely. The constructed paragraph renders strangely.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -492,12 +515,14 @@ You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this, When you try this,
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-span)" region="select-span"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-span)" region="select-span">
</code-example> </code-example>
the drop down is empty. the drop down is empty.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -512,12 +537,14 @@ because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`. Here's the conditional paragraph again, this time using `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example> </code-example>
It renders properly. It renders properly.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -525,12 +552,14 @@ It renders properly.
Now conditionally exclude a _select_ `<option>` with `<ng-container>`. Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example> </code-example>
The drop down works properly. The drop down works properly.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -539,6 +568,7 @@ The `<ng-container>` is a syntax element recognized by the Angular parser.
It's not a directive, component, class, or interface. It's not a directive, component, class, or interface.
It's more like the curly braces in a JavaScript `if`-block: It's more like the curly braces in a JavaScript `if`-block:
<code-example language="javascript"> <code-example language="javascript">
if (someCondition) { if (someCondition) {
statement1; statement1;
@ -563,6 +593,7 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***. `UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1">
</code-example> </code-example>
@ -580,6 +611,7 @@ Creating a directive is similar to creating a component.
Here's how you might begin: Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton">
</code-example> </code-example>
@ -611,6 +643,7 @@ and access the _view container_ through a
You inject both in the directive constructor as private variables of the class. You inject both in the directive constructor as private variables of the class.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor">
</code-example> </code-example>
@ -630,6 +663,7 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax) guide.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (set)" region="set"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (set)" region="set">
</code-example> </code-example>
@ -648,6 +682,7 @@ Nobody reads the `myUnless` property so it doesn't need a getter.
The completed directive code looks like this: The completed directive code looks like this:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (excerpt)" region="no-docs"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example> </code-example>
@ -657,6 +692,7 @@ Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleV
Then create some HTML to try it. Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless">
</code-example> </code-example>
@ -664,6 +700,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 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. When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -679,38 +716,46 @@ You can both try and download the source code for this guide in the <live-exampl
Here is the source from the `src/app/` folder. Here is the source from the `src/app/` folder.
<code-tabs> <code-tabs>
<code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts"> <code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app.component.html" path="structural-directives/src/app/app.component.html"> <code-pane title="app.component.html" path="structural-directives/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="app.component.css" path="structural-directives/src/app/app.component.css"> <code-pane title="app.component.css" path="structural-directives/src/app/app.component.css">
</code-pane> </code-pane>
<code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts"> <code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="hero.ts" path="structural-directives/src/app/hero.ts"> <code-pane title="hero.ts" path="structural-directives/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts"> <code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts">
</code-pane> </code-pane>
<code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts"> <code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts">
</code-pane> </code-pane>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -86,23 +86,28 @@ through the global `ng` object.
Anything you can import from `@angular` is a nested member of this `ng` object: Anything you can import from `@angular` is a nested member of this `ng` object:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="ng2import"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import">
</code-pane> </code-pane>
@ -138,23 +143,28 @@ to limit unintentional leaking of private symbols into the global scope.
Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants. Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="appexport"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport">
</code-pane> </code-pane>
@ -169,23 +179,28 @@ In _TypeScript_ and _ES6_ apps, you `import` things that have been exported from
In _ES5_ you use the shared namespace object to access "exported" entities from other files. In _ES5_ you use the shared namespace object to access "exported" entities from other files.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="appimport"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport">
</code-pane> </code-pane>
@ -235,23 +250,28 @@ _ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype. Use the constructor function pattern instead, adding methods to the prototype.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="class"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="class">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto">
</code-pane> </code-pane>
@ -275,23 +295,28 @@ In _ES5_, you also provide an `annotations` array but you attach it to the _cons
See these variations side-by-side: See these variations side-by-side:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="metadata"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata">
</code-pane> </code-pane>
@ -303,29 +328,35 @@ See these variations side-by-side:
A large component template is often kept in a separate template file. A large component template is often kept in a separate template file.
<code-example path="cb-ts-to-js/ts/src/app/hero-title.component.html" linenums="false"> <code-example path="cb-ts-to-js/ts/src/app/hero-title.component.html" linenums="false">
</code-example> </code-example>
The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property: The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6" region="templateUrl"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl">
</code-pane> </code-pane>
@ -354,13 +385,16 @@ Here is an example of the `HeroComponent`, re-written with the DSL,
next to the original _ES5_ version for comparison: next to the original _ES5_ version for comparison:
<code-tabs> <code-tabs>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js">
</code-pane> </code-pane>
@ -373,6 +407,7 @@ next to the original _ES5_ version for comparison:
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
<header> <header>
Name the constructor Name the constructor
</header> </header>
@ -391,6 +426,7 @@ _TypeScript_ and _ES6_ support with getters and setters.
Here's an example of a read-only _TypeScript_ property with a getter Here's an example of a read-only _TypeScript_ property with a getter
that prepares a toggle-button label for the next clicked state: that prepares a toggle-button label for the next clicked state:
<code-example path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="defined-property" linenums="false"> <code-example path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="defined-property" linenums="false">
</code-example> </code-example>
@ -402,6 +438,7 @@ The _ES5 DSL_ does not support _defined properties_ directly
but you can still create them by extracting the "class" prototype and but you can still create them by extracting the "class" prototype and
adding the _defined property_ in raw JavaScript like this: adding the _defined property_ in raw JavaScript like this:
<code-example path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="defined-property" linenums="false"> <code-example path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="defined-property" linenums="false">
</code-example> </code-example>
@ -410,6 +447,7 @@ adding the _defined property_ in raw JavaScript like this:
There are similar DSLs for other decorated classes. There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`: You can define a directive with `ng.core.Directive`:
<code-example> <code-example>
app.MyDirective = ng.core.Directive({ app.MyDirective = ng.core.Directive({
selector: '[myDirective]' selector: '[myDirective]'
@ -419,6 +457,7 @@ You can define a directive with `ng.core.Directive`:
</code-example> </code-example>
and a pipe with `ng.core.Pipe`: and a pipe with `ng.core.Pipe`:
<code-example> <code-example>
app.MyPipe = ng.core.Pipe({ app.MyPipe = ng.core.Pipe({
name: 'myPipe' name: 'myPipe'
@ -444,28 +483,34 @@ They have no physical manifestation in the generated JavaScript code.
Just implement the methods and ignore interfaces when translating code samples from _TypeScript_ to JavaScript. Just implement the methods and ignore interfaces when translating code samples from _TypeScript_ to JavaScript.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js" region="dsl">
</code-pane> </code-pane>
@ -495,28 +540,34 @@ But note that what would have been _separate_ `@Input` and `@Output` property de
combined in the metadata `inputs` and `outputs` _arrays_. combined in the metadata `inputs` and `outputs` _arrays_.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl">
</code-pane> </code-pane>
@ -567,28 +618,34 @@ last parameter is the class constructor itself.
This format should be familiar to AngularJS developers. This format should be familiar to AngularJS developers.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di.component.js" region="dsl">
</code-pane> </code-pane>
@ -615,28 +672,34 @@ When writing with _ES5 DSL_, set the `Class.constructor` property to a function
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter. array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js" region="dsl">
</code-pane> </code-pane>
@ -665,28 +728,34 @@ When writing with _ES5 DSL_, set the `Class.constructor` property to a function
array as before. Use a nested array to define a parameter's complete injection specification. array as before. Use a nested array to define a parameter's complete injection specification.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="dsl">
</code-pane> </code-pane>
@ -733,28 +802,34 @@ The `host` value is an object whose properties are host property and listener b
* Each value identifies the corresponding component property or method. * Each value identifies the corresponding component property or method.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-host.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-host.component.js" region="dsl">
</code-pane> </code-pane>
@ -771,13 +846,16 @@ The following re-implementation of the `HeroComponent` reminds us that _any prop
can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_. can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_.
These particular _TypeScript_ and _ES6_ code snippets happen to be identical. These particular _TypeScript_ and _ES6_ code snippets happen to be identical.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6">
</code-pane> </code-pane>
@ -815,23 +893,28 @@ The `queries` property value is a hash map.
* each _value_ is a new instance of either `ViewChild` or `ViewChildren`. * each _value_ is a new instance of either `ViewChild` or `ViewChildren`.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="view"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="view">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="view"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="view">
</code-pane> </code-pane>
@ -848,23 +931,28 @@ They can be added in the same way as [`@ViewChild`](api/core/index/ViewChild-dec
[`@ViewChildren`](api/core/index/ViewChildren-decorator). [`@ViewChildren`](api/core/index/ViewChildren-decorator).
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="content"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="content">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="content"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="content">
</code-pane> </code-pane>

View File

@ -55,6 +55,7 @@ You can set a variable's type to `any` even when the `noImplicitAny` flag is `tr
When the `noImplicitAny` flag is `true`, you may get *implicit index errors* as well. When the `noImplicitAny` flag is `true`, you may get *implicit index errors* as well.
Most developers feel that *this particular error* is more annoying than helpful. Most developers feel that *this particular error* is more annoying than helpful.
You can suppress them with the following additional flag: You can suppress them with the following additional flag:
<code-example format="."> <code-example format=".">
"suppressImplicitAnyIndexErrors":true "suppressImplicitAnyIndexErrors":true
@ -92,6 +93,7 @@ like `Promise` if the target is `es6`.
Since the QuickStart is targeting `es5`, you can override the Since the QuickStart is targeting `es5`, you can override the
list of declaration files to be included: list of declaration files to be included:
<code-example format="."> <code-example format=".">
"lib": ["es2015", "dom"] "lib": ["es2015", "dom"]

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@ To bind to a DOM event, surround the DOM event name in parentheses and assign a
The following example shows an event binding that implements a click handler: The following example shows an event binding that implements a click handler:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false"> <code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false">
</code-example> </code-example>
@ -38,6 +39,7 @@ usually the Angular component controlling the template.
The example above shows a single line of HTML, but that HTML belongs to a larger component: The example above shows a single line of HTML, but that HTML belongs to a larger component:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false"> <code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false">
</code-example> </code-example>
@ -50,6 +52,7 @@ This section shows how to bind to the `keyup` event of an input box to get the u
The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler. The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false">
</code-example> </code-example>
@ -57,6 +60,7 @@ The following code listens to the `keyup` event and passes the entire event payl
When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding
DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method. DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" linenums="false">
</code-example> </code-example>
@ -76,11 +80,13 @@ displays the accumulating input box changes from the `values` property.
Suppose the user enters the letters "abc", and then backspaces to remove them one by one. Suppose the user enters the letters "abc", and then backspaces to remove them one by one.
Here's what the UI displays: Here's what the UI displays:
<code-example> <code-example>
a | ab | abc | ab | a | | a | ab | abc | ab | a | |
</code-example> </code-example>
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -91,6 +97,7 @@ Here's what the UI displays:
Alternatively, you could accumulate the individual keys themselves by substituting `event.key` Alternatively, you could accumulate the individual keys themselves by substituting `event.key`
for `event.target.value` in which case the same user input would produce: for `event.target.value` in which case the same user input would produce:
<code-example> <code-example>
a | b | c | backspace | backspace | backspace | a | b | c | backspace | backspace | backspace |
@ -112,6 +119,7 @@ that could reveal properties of the event object and prevent silly mistakes.
The following example rewrites the method with types: The following example rewrites the method with types:
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false">
</code-example> </code-example>
@ -138,6 +146,7 @@ To declare a template reference variable, precede an identifier with a hash (or
The following example uses a template reference variable The following example uses a template reference variable
to implement a keystroke loopback in a simple template. to implement a keystroke loopback in a simple template.
<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" linenums="false"> <code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" linenums="false">
</code-example> </code-example>
@ -152,6 +161,7 @@ and the component does nothing.
Type something in the input box, and watch the display update with each keystroke. Type something in the input box, and watch the display update with each keystroke.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -175,6 +185,7 @@ It's easier to get to the input box with the template reference
variable than to go through the `$event` object. Here's a rewrite of the previous variable than to go through the `$event` object. Here's a rewrite of the previous
`keyup` example that uses a template reference variable to get the user's input. `keyup` example that uses a template reference variable to get the user's input.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" linenums="false">
</code-example> </code-example>
@ -190,11 +201,13 @@ One way to reduce the noise would be to examine every `$event.keyCode` and take
There's an easier way: bind to Angular's `keyup.enter` pseudo-event. There's an easier way: bind to Angular's `keyup.enter` pseudo-event.
Then Angular calls the event handler only when the user presses _Enter_. Then Angular calls the event handler only when the user presses _Enter_.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" linenums="false">
</code-example> </code-example>
Here's how it works. Here's how it works.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -210,6 +223,7 @@ The component's `value` property is updated only when the user presses _Enter_.
To fix this issue, listen to both the _Enter_ key and the _blur_ event. To fix this issue, listen to both the _Enter_ key and the _blur_ event.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false">
</code-example> </code-example>
@ -224,6 +238,7 @@ that can display a list of heroes and add new heroes to the list.
The user can add a hero by typing the hero's name in the input box and The user can add a hero by typing the hero's name in the input box and
clicking **Add**. clicking **Add**.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -231,6 +246,7 @@ clicking **Add**.
Below is the "Little Tour of Heroes" component. Below is the "Little Tour of Heroes" component.
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false"> <code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false">
</code-example> </code-example>
@ -254,23 +270,28 @@ clears the input box after a new hero is added to the list.
Following is all the code discussed in this page. Following is all the code discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts"> <code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts">
</code-pane> </code-pane>
<code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts"> <code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts">
</code-pane> </code-pane>
<code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts"> <code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts">
</code-pane> </code-pane>
<code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts"> <code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts">
</code-pane> </code-pane>

View File

@ -46,6 +46,7 @@ The steps are as follows:
- [Step 5](guide/visual-studio-2015#build-and-run): Build and run the app - [Step 5](guide/visual-studio-2015#build-and-run): Build and run the app
<h2 id='prereq1'> <h2 id='prereq1'>
Prerequisite: Node.js Prerequisite: Node.js
</h2> </h2>
@ -64,6 +65,7 @@ Older versions produce errors.
<h2 id='prereq2'> <h2 id='prereq2'>
Prerequisite: Visual Studio 2015 Update 3 Prerequisite: Visual Studio 2015 Update 3
</h2> </h2>
@ -76,6 +78,7 @@ If you don't have it, install **[Visual Studio 2015 Update 3](https://www.visual
Or use `Tools | Extensions and Updates` to update to Update 3 directly from Visual Studio 2015. Or use `Tools | Extensions and Updates` to update to Update 3 directly from Visual Studio 2015.
<h2 id='prereq3'> <h2 id='prereq3'>
Prerequisite: Configure External Web tools Prerequisite: Configure External Web tools
</h2> </h2>
@ -94,6 +97,7 @@ if not found then look in the global path and if it is not found there, Visual S
will use its own versions of the tools. will use its own versions of the tools.
<h2 id='prereq4'> <h2 id='prereq4'>
Prerequisite: Install TypeScript 2 for Visual Studio 2015 Prerequisite: Install TypeScript 2 for Visual Studio 2015
</h2> </h2>
@ -111,6 +115,7 @@ At this point, Visual Studio is ready. Its a good idea to close Visual Studio
restart it to make sure everything is clean. restart it to make sure everything is clean.
<h2 id='download'> <h2 id='download'>
Step 1: Download the QuickStart files Step 1: Download the QuickStart files
</h2> </h2>
@ -119,6 +124,7 @@ restart it to make sure everything is clean.
from github. If you downloaded as a zip file, extract the files. from github. If you downloaded as a zip file, extract the files.
<h2 id='create-project'> <h2 id='create-project'>
Step 2: Create the Visual Studio ASP.NET project Step 2: Create the Visual Studio ASP.NET project
</h2> </h2>
@ -141,6 +147,7 @@ no authentication and no hosting. Pick the template and options appropriate for
<h2 id='copy'> <h2 id='copy'>
Step 3: Copy the QuickStart files into the ASP.NET project folder Step 3: Copy the QuickStart files into the ASP.NET project folder
</h2> </h2>
@ -158,6 +165,7 @@ Include the files in the Visual Studio project as follows:
* tsconfig.json * tsconfig.json
<h2 id='restore'> <h2 id='restore'>
Step 4: Restore the required packages Step 4: Restore the required packages
</h2> </h2>
@ -174,6 +182,7 @@ Restore the packages required for an Angular application as follows:
* **Do not** include the `node_modules` folder in the project. Let it be a hidden project folder. * **Do not** include the `node_modules` folder in the project. Let it be a hidden project folder.
<h2 id='build-and-run'> <h2 id='build-and-run'>
Step 5: Build and run the app Step 5: Build and run the app
</h2> </h2>
@ -195,6 +204,7 @@ Try editing any of the project files. *Save* and refresh the browser to
see the changes. see the changes.
<h2 id='routing'> <h2 id='routing'>
Note on Routing Applications Note on Routing Applications
</h2> </h2>

View File

@ -6,6 +6,7 @@ Create Angular applications with a Webpack based tooling.
@description @description
<style> <style>
h4 {font-size: 17px !important; text-transform: none !important;} h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; } .syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -69,6 +70,7 @@ You supply Webpack with one or more *entry* files and let it find and incorporat
The one entry point file in this example is the application's root file, `src/main.ts`: The one entry point file in this example is the application's root file, `src/main.ts`:
<code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false">
</code-example> </code-example>
@ -76,6 +78,7 @@ The one entry point file in this example is the application's root file, `src/ma
Webpack inspects that file and traverses its `import` dependencies recursively. Webpack inspects that file and traverses its `import` dependencies recursively.
<code-example path="webpack/src/app/app.component.ts" region="component" linenums="false"> <code-example path="webpack/src/app/app.component.ts" region="component" linenums="false">
</code-example> </code-example>
@ -85,8 +88,10 @@ It opens the `@angular/core` file and follows _its_ network of `import` statemen
Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration: Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration:
<div class='code-example'> <div class='code-example'>
<code-example name="webpack.config.js (single output)" language="javascript"> <code-example name="webpack.config.js (single output)" language="javascript">
output: { output: {
filename: 'app.js' filename: 'app.js'
@ -108,8 +113,10 @@ It's preferable to separate the volatile application app code from comparatively
Change the configuration so that it has two entry points, `main.ts` and `vendor.ts`: Change the configuration so that it has two entry points, `main.ts` and `vendor.ts`:
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
entry: { entry: {
app: 'src/app.ts', app: 'src/app.ts',
@ -141,6 +148,7 @@ The `[name]` in the output name is a *placeholder* that a Webpack plugin replace
To tell Webpack what belongs in the vendor bundle, To tell Webpack what belongs in the vendor bundle,
add a `vendor.ts` file that only imports the application's third-party modules: add a `vendor.ts` file that only imports the application's third-party modules:
<code-example path="webpack/src/vendor.ts" linenums="false"> <code-example path="webpack/src/vendor.ts" linenums="false">
</code-example> </code-example>
@ -156,8 +164,10 @@ Webpack _itself_ only understands JavaScript files.
Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*. Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
Configure loaders for TypeScript and CSS as follows. Configure loaders for TypeScript and CSS as follows.
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
rules: [ rules: [
{ {
@ -178,8 +188,10 @@ Configure loaders for TypeScript and CSS as follows.
When Webpack encounters `import` statements like the following, When Webpack encounters `import` statements like the following,
it applies the `test` RegEx patterns. it applies the `test` RegEx patterns.
<div class='code-example'> <div class='code-example'>
<code-example language="typescript"> <code-example language="typescript">
import { AppComponent } from './app.component.ts'; import { AppComponent } from './app.component.ts';
@ -208,8 +220,10 @@ Then it applies the `style` loader to append the css inside `<style>` elements o
Webpack has a build pipeline with well-defined phases. Webpack has a build pipeline with well-defined phases.
Tap into that pipeline with plugins such as the `uglify` minification plugin: Tap into that pipeline with plugins such as the `uglify` minification plugin:
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
plugins: [ plugins: [
new webpack.optimize.UglifyJsPlugin() new webpack.optimize.UglifyJsPlugin()
@ -231,6 +245,7 @@ After that brief orientation, you are ready to build your own Webpack configurat
Begin by setting up the development environment. Begin by setting up the development environment.
Create a new project folder. Create a new project folder.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
mkdir angular-webpack mkdir angular-webpack
cd angular-webpack cd angular-webpack
@ -240,28 +255,34 @@ Create a new project folder.
Add these files: Add these files:
<code-tabs> <code-tabs>
<code-pane title="package.json" path="webpack/package.webpack.json"> <code-pane title="package.json" path="webpack/package.webpack.json">
</code-pane> </code-pane>
<code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json"> <code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json">
</code-pane> </code-pane>
<code-pane title="webpack.config.js" path="webpack/webpack.config.js"> <code-pane title="webpack.config.js" path="webpack/webpack.config.js">
</code-pane> </code-pane>
<code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js"> <code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js">
</code-pane> </code-pane>
<code-pane title="config/helpers.js" path="webpack/config/helpers.js"> <code-pane title="config/helpers.js" path="webpack/config/helpers.js">
</code-pane> </code-pane>
@ -284,6 +305,7 @@ They are listed in the updated `packages.json`.
~~~ ~~~
Open a terminal window and install the npm packages. Open a terminal window and install the npm packages.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm install npm install
@ -302,6 +324,7 @@ Polyfills should be bundled separately from the application and vendor bundles.
Add a `polyfills.ts` like this one to the `src/` folder. Add a `polyfills.ts` like this one to the `src/` folder.
<code-example path="webpack/src/polyfills.ts" linenums="false"> <code-example path="webpack/src/polyfills.ts" linenums="false">
</code-example> </code-example>
@ -311,6 +334,7 @@ Add a `polyfills.ts` like this one to the `src/` folder.
~~~ {.callout.is-critical} ~~~ {.callout.is-critical}
<header> <header>
Loading polyfills Loading polyfills
</header> </header>
@ -334,6 +358,7 @@ All three have a lot of configuration in common.
Gather the common configuration in a file called `webpack.common.js`. Gather the common configuration in a file called `webpack.common.js`.
<code-example path="webpack/config/webpack.common.js" linenums="false"> <code-example path="webpack/config/webpack.common.js" linenums="false">
</code-example> </code-example>
@ -359,6 +384,7 @@ and exports several objects as properties of a `module.exports` object.
The first export is the `entry` object: The first export is the `entry` object:
<code-example path="webpack/config/webpack.common.js" region="entries" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="entries" linenums="false">
</code-example> </code-example>
@ -376,8 +402,10 @@ This `entry` object defines the three bundles:
The app will `import` dozens if not hundreds of JavaScript and TypeScript files. The app will `import` dozens if not hundreds of JavaScript and TypeScript files.
You could write `import` statements with explicit extensions like this example: You could write `import` statements with explicit extensions like this example:
<div class='code-example'> <div class='code-example'>
<code-example language="typescript"> <code-example language="typescript">
import { AppComponent } from './app.component.ts'; import { AppComponent } from './app.component.ts';
@ -391,6 +419,7 @@ Tell Webpack to resolve extension-less file requests by looking for matching fil
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files). `.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
<code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false">
</code-example> </code-example>
@ -412,6 +441,7 @@ add `.css` and `.html` to the list.
Rules tell Webpack which loaders to use for each file, or module: Rules tell Webpack which loaders to use for each file, or module:
<code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false">
</code-example> </code-example>
@ -451,6 +481,7 @@ Multiple loaders can be chained using the array notation.
Finally, create instances of three plugins: Finally, create instances of three plugins:
<code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false">
</code-example> </code-example>
@ -503,6 +534,7 @@ These files tend to be short and simple.
Here is the `webpack.dev.js` development configuration file. Here is the `webpack.dev.js` development configuration file.
<code-example path="webpack/config/webpack.dev.js" linenums="false"> <code-example path="webpack/config/webpack.dev.js" linenums="false">
</code-example> </code-example>
@ -525,6 +557,7 @@ other configuration options in this file.
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -539,6 +572,7 @@ Grab the app code at the end of this guide and try:
Configuration of a *production* build resembles *development* configuration with a few key changes. Configuration of a *production* build resembles *development* configuration with a few key changes.
<code-example path="webpack/config/webpack.prod.js" linenums="false"> <code-example path="webpack/config/webpack.prod.js" linenums="false">
</code-example> </code-example>
@ -562,12 +596,14 @@ There are additional plugins:
Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this: Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this:
<code-example path="webpack/src/main.ts" region="enable-prod" linenums="false"> <code-example path="webpack/src/main.ts" region="enable-prod" linenums="false">
</code-example> </code-example>
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm run build npm run build
@ -588,6 +624,7 @@ You could merge the test configuration into the `webpack.common` configuration a
But it might be simpler to start over with a completely fresh configuration. But it might be simpler to start over with a completely fresh configuration.
<code-example path="webpack/config/webpack.test.js" linenums="false"> <code-example path="webpack/config/webpack.test.js" linenums="false">
</code-example> </code-example>
@ -595,6 +632,7 @@ But it might be simpler to start over with a completely fresh configuration.
Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests: Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests:
<code-example path="webpack/config/karma.conf.js" linenums="false"> <code-example path="webpack/config/karma.conf.js" linenums="false">
</code-example> </code-example>
@ -606,6 +644,7 @@ The `karma-test-shim` tells Karma what files to pre-load and
primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded. primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded.
<code-example path="webpack/config/karma-test-shim.js" linenums="false"> <code-example path="webpack/config/karma-test-shim.js" linenums="false">
</code-example> </code-example>
@ -616,6 +655,7 @@ Each spec file imports all&mdash;and only&mdash;the application source code that
Webpack loads just _those_ specific application files and ignores the other files that you aren't testing. Webpack loads just _those_ specific application files and ignores the other files that you aren't testing.
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm test npm test
@ -627,18 +667,22 @@ Here is the source code for a small application that bundles with the
Webpack techniques covered in this guide. Webpack techniques covered in this guide.
<code-tabs> <code-tabs>
<code-pane title="src/index.html" path="webpack/src/index.html"> <code-pane title="src/index.html" path="webpack/src/index.html">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="webpack/src/main.ts"> <code-pane title="src/main.ts" path="webpack/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css"> <code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css">
</code-pane> </code-pane>
@ -648,28 +692,34 @@ Webpack techniques covered in this guide.
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css"> <code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts"> <code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts">
</code-pane> </code-pane>
@ -687,13 +737,16 @@ on the image and download it to that folder.
{@a bundle-ts} {@a bundle-ts}
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles. Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
<code-tabs> <code-tabs>
<code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts"> <code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts">
</code-pane> </code-pane>
<code-pane title="src/vendor.ts" path="webpack/src/vendor.ts"> <code-pane title="src/vendor.ts" path="webpack/src/vendor.ts">
</code-pane> </code-pane>

View File

@ -1,19 +1,24 @@
@description @description
<div flex=true> <div flex=true>
<p> <p>
What's your question about? What's your question about?
</p> </p>
<select id='feedback-dropdown' name="Angular Version"> <select id='feedback-dropdown' name="Angular Version">
<option value="Angular"> <option value="Angular">
Angular Angular
</option> </option>
<option value="AngularJS"> <option value="AngularJS">
AngularJS AngularJS
</option> </option>
@ -22,6 +27,7 @@
</select> </select>
<button id='feedback-btn'> <button id='feedback-btn'>
Submit Submit
</button> </button>

View File

@ -1,47 +1,57 @@
@description @description
<div class='clearfix'> <div class='clearfix'>
<a class='card' class='c4' href="/docs/#{lang}/#{vers}/quickstart.html"> <a class='card' class='c4' href="/docs/#{lang}/#{vers}/quickstart.html">
<h2 class='text-headline' class='text-uppercase'> <h2 class='text-headline' class='text-uppercase'>
Quickstart Quickstart
</h2> </h2>
<p> <p>
A short beginner guide explaining the basic concepts of Angular A short beginner guide explaining the basic concepts of Angular
</p> </p>
<footer> <footer>
View Quickstart View Quickstart
</footer> </footer>
</a> <a class='card' class='c4' href="/docs/#{lang}/#{vers}/guide/"> </a> <a class='card' class='c4' href="/docs/#{lang}/#{vers}/guide/">
<h2 class='text-headline' class='text-uppercase'> <h2 class='text-headline' class='text-uppercase'>
Developer Guide Developer Guide
</h2> </h2>
<p> <p>
An intermediate development guide covering all major features of Angular An intermediate development guide covering all major features of Angular
</p> </p>
<footer> <footer>
View Guide View Guide
</footer> </footer>
</a> <a class='card' class='c4' href="/docs/#{lang}/#{vers}/api/"> </a> <a class='card' class='c4' href="/docs/#{lang}/#{vers}/api/">
<h2 class='text-headline' class='text-uppercase'> <h2 class='text-headline' class='text-uppercase'>
API Reference API Reference
</h2> </h2>
<p> <p>
An advanced reference of all Angular Classes, Methods, etc. An advanced reference of all Angular Classes, Methods, etc.
</p> </p>
<footer> <footer>
View API View API
</footer> </footer>
@ -50,40 +60,49 @@
</div> </div>
<div class='c4' class='secondary-content-list'> <div class='c4' class='secondary-content-list'>
<h4> <h4>
Advanced Documentation Advanced Documentation
</h4> </h4>
<ul> <ul>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/animations.html"> Animations </a> <a href="/docs/#{lang}/#{vers}/guide/animations.html"> Animations </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/attribute-directives.html"> Attribute Directives </a> <a href="/docs/#{lang}/#{vers}/guide/attribute-directives.html"> Attribute Directives </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/browser-support.html"> Browser Support </a> <a href="/docs/#{lang}/#{vers}/guide/browser-support.html"> Browser Support </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/component-styles.html"> Component Styles </a> <a href="/docs/#{lang}/#{vers}/guide/component-styles.html"> Component Styles </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/deployment.html"> Deployment </a> <a href="/docs/#{lang}/#{vers}/guide/deployment.html"> Deployment </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/guide/animations.html"> View All... </a> <a href="/docs/#{lang}/#{vers}/guide/animations.html"> View All... </a>
</li> </li>
@ -95,35 +114,43 @@
</div> </div>
<div class='c4' class='secondary-content-list'> <div class='c4' class='secondary-content-list'>
<h4> <h4>
Cookbook Cookbook
</h4> </h4>
<ul> <ul>
<li> <li>
<a href="/docs/#{lang}/#{vers}/cookbook/aot-compiler.html"> Ahead-of-time Compilation </a> <a href="/docs/#{lang}/#{vers}/cookbook/aot-compiler.html"> Ahead-of-time Compilation </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/cookbook/ajs-quick-reference.html"> AngularJS to Angular </a> <a href="/docs/#{lang}/#{vers}/cookbook/ajs-quick-reference.html"> AngularJS to Angular </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/cookbook/component-communication.html"> Component Interaction </a> <a href="/docs/#{lang}/#{vers}/cookbook/component-communication.html"> Component Interaction </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/cookbook/dependency-injection.html"> Dependency Injection </a> <a href="/docs/#{lang}/#{vers}/cookbook/dependency-injection.html"> Dependency Injection </a>
</li> </li>
<li> <li>
<a href="/docs/#{lang}/#{vers}/cookbook/"> View All... </a> <a href="/docs/#{lang}/#{vers}/cookbook/"> View All... </a>
</li> </li>
@ -135,40 +162,49 @@
</div> </div>
<div class='c4' class='secondary-content-list'> <div class='c4' class='secondary-content-list'>
<h4> <h4>
Tools & Libraries Tools & Libraries
</h4> </h4>
<ul> <ul>
<li> <li>
<a target="_blank" href="https://github.com/angular/universal"> Angular Universal </a> <a target="_blank" href="https://github.com/angular/universal"> Angular Universal </a>
</li> </li>
<li> <li>
<a target="_blank" href="https://augury.angular.io/"> Augury </a> <a target="_blank" href="https://augury.angular.io/"> Augury </a>
</li> </li>
<li> <li>
<a target="_blank" href="https://github.com/jaxio/celerio-angular-quickstart"> Celerio Angular Quickstart </a> <a target="_blank" href="https://github.com/jaxio/celerio-angular-quickstart"> Celerio Angular Quickstart </a>
</li> </li>
<li> <li>
<a target="_blank" href="https://github.com/mgechev/codelyzer"> Codelyzer </a> <a target="_blank" href="https://github.com/mgechev/codelyzer"> Codelyzer </a>
</li> </li>
<li> <li>
<a target="_blank" href="https://github.com/johnpapa/lite-server"> Lite-server </a> <a target="_blank" href="https://github.com/johnpapa/lite-server"> Lite-server </a>
</li> </li>
<li> <li>
<a target="_blank" href="/resources/"> View All... </a> <a target="_blank" href="/resources/"> View All... </a>
</li> </li>

View File

@ -4,6 +4,7 @@ Angular applications are made up of _components_.
A _component_ is the combination of an HTML template and a component class that controls a portion of the screen. Here is an example of a component that displays a simple string: A _component_ is the combination of an HTML template and a component class that controls a portion of the screen. Here is an example of a component that displays a simple string:
<code-example path="quickstart/src/app/app.component.ts" linenums="false"> <code-example path="quickstart/src/app/app.component.ts" linenums="false">
</code-example> </code-example>
@ -26,6 +27,7 @@ Every component begins with an `@Component` [!{_decorator}](glossary)
The `selector` property tells Angular to display the component inside a custom `<my-app>` tag in the `index.html`. The `selector` property tells Angular to display the component inside a custom `<my-app>` tag in the `index.html`.
<code-example path="quickstart/src/index.html" region="my-app" linenums="false"> <code-example path="quickstart/src/index.html" region="my-app" linenums="false">
</code-example> </code-example>

View File

@ -1,38 +1,46 @@
@description @description
<h2> <h2>
Victor Savkin's Blog Posts Victor Savkin's Blog Posts
</h2> </h2>
<ul> <ul>
<li> <li>
<a href="http://victorsavkin.com/post/137821436516/managing-state-in-angular-2-applications"> Managing State in Angular 2 Applications </a> <a href="http://victorsavkin.com/post/137821436516/managing-state-in-angular-2-applications"> Managing State in Angular 2 Applications </a>
</li> </li>
<li> <li>
<a href="http://victorsavkin.com/post/114168430846/two-phases-of-angular-2-applications">Two Phases of Angular 2 Applications</a> <a href="http://victorsavkin.com/post/114168430846/two-phases-of-angular-2-applications">Two Phases of Angular 2 Applications</a>
</li> </li>
<li> <li>
<a href="http://angularjs.blogspot.com/2015/03/forms-in-angular-2.html">Forms in Angular 2</a> <a href="http://angularjs.blogspot.com/2015/03/forms-in-angular-2.html">Forms in Angular 2</a>
</li> </li>
<li> <li>
<a href="http://victorsavkin.com/post/110170125256/change-detection-in-angular-2">Change detection</a> <a href="http://victorsavkin.com/post/110170125256/change-detection-in-angular-2">Change detection</a>
</li> </li>
<li> <li>
<a href="http://victorsavkin.com/post/108837493941/better-support-for-functional-programming-in">Functional programming </a> <a href="http://victorsavkin.com/post/108837493941/better-support-for-functional-programming-in">Functional programming </a>
</li> </li>
<li> <li>
<a href="http://victorsavkin.com/post/102965317996/angular-2-bits-unified-dependency-injection">Dependency injection</a> <a href="http://victorsavkin.com/post/102965317996/angular-2-bits-unified-dependency-injection">Dependency injection</a>
</li> </li>
@ -42,23 +50,28 @@
<h2> <h2>
<span class="icon-play-circle-outline"></span> Videos <span class="icon-play-circle-outline"></span> Videos
</h2> </h2>
<h4> <h4>
Intro Videos Intro Videos
</h4> </h4>
<ul> <ul>
<li> <li>
<a href="https://www.youtube.com/watch?v=uD6Okha_Yj0">Building a Todo App</a> by David East <a href="https://www.youtube.com/watch?v=uD6Okha_Yj0">Building a Todo App</a> by David East
</li> </li>
<li> <li>
<a href="https://www.youtube.com/watch?v=4C4bmDOV5hk">Angular 2 Forms</a> by David East <a href="https://www.youtube.com/watch?v=4C4bmDOV5hk">Angular 2 Forms</a> by David East
</li> </li>
@ -67,33 +80,40 @@
</ul> </ul>
<h4> <h4>
ng-conf ng-conf
</h4> </h4>
<ul> <ul>
<li> <li>
Playlist <a href="https://www.youtube.com/watch?v=QHulaj5ZxbI&index=1&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">of ng-conf 2015 videos</a>. Playlist <a href="https://www.youtube.com/watch?v=QHulaj5ZxbI&index=1&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">of ng-conf 2015 videos</a>.
</li> </li>
<li> <li>
<a href="https://www.youtube.com/watch?v=QHulaj5ZxbI&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Day 1 Keynote</a>: a broad overview of Angular 2, migration, and where we are headed. <a href="https://www.youtube.com/watch?v=QHulaj5ZxbI&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Day 1 Keynote</a>: a broad overview of Angular 2, migration, and where we are headed.
</li> </li>
<li> <li>
<a href="https://www.youtube.com/watch?v=-dMBcqwvYA0&index=21&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Day 2 Keynote</a>: Misko and Rado do a deep-dive on Angular 2 details. <a href="https://www.youtube.com/watch?v=-dMBcqwvYA0&index=21&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Day 2 Keynote</a>: Misko and Rado do a deep-dive on Angular 2 details.
</li> </li>
<li> <li>
<a href="https://www.youtube.com/watch?v=AbunztfV5vU&index=6&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Creating Container Components with Web Components in Angular</a>: Kara Erickson &amp; Rachael L Moore. <a href="https://www.youtube.com/watch?v=AbunztfV5vU&index=6&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Creating Container Components with Web Components in Angular</a>: Kara Erickson &amp; Rachael L Moore.
</li> </li>
<li> <li>
<a href="https://www.youtube.com/watch?v=jvKGQSFQf10&index=31&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Change Detection Reinvented</a>: Why Angular 2 change detection is fast out of the box and options for developers to make it even faster. <a href="https://www.youtube.com/watch?v=jvKGQSFQf10&index=31&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">Change Detection Reinvented</a>: Why Angular 2 change detection is fast out of the box and options for developers to make it even faster.
</li> </li>
@ -102,13 +122,16 @@
</ul> </ul>
<h4> <h4>
ng-europe ng-europe
</h4> </h4>
<ul> <ul>
<li> <li>
Oct 2014 <a href="https://www.youtube.com/watch?v=lGdnh8QSPPk&list=PLhc_bKwZngxW_ZlY0NkaGkvKpiA_pzcZ-">playlist of ng-europe videos on Angular 2</a> and the future of Angular. Oct 2014 <a href="https://www.youtube.com/watch?v=lGdnh8QSPPk&list=PLhc_bKwZngxW_ZlY0NkaGkvKpiA_pzcZ-">playlist of ng-europe videos on Angular 2</a> and the future of Angular.
</li> </li>
@ -118,33 +141,40 @@
<h2> <h2>
<span class="icon-content-copy"></span> API Design Docs &amp; Notes <span class="icon-content-copy"></span> API Design Docs &amp; Notes
</h2> </h2>
<ul> <ul>
<li> <li>
<a href="https://drive.google.com/open?id=0B7GYXx6a6d8QR3lTT1J3MEpRSlE&authuser=0">Best Practices</a> <a href="https://drive.google.com/open?id=0B7GYXx6a6d8QR3lTT1J3MEpRSlE&authuser=0">Best Practices</a>
</li> </li>
<li> <li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbacUnUxc3l5aTZrbVk&authuser=0">API Design Docs</a> <a href="https://drive.google.com/open?id=0BxgtL8yFJbacUnUxc3l5aTZrbVk&authuser=0">API Design Docs</a>
</li> </li>
<li> <li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbacMEZDc2NtWS1VZ1k&authuser=0">Meeting Notes</a> <a href="https://drive.google.com/open?id=0BxgtL8yFJbacMEZDc2NtWS1VZ1k&authuser=0">Meeting Notes</a>
</li> </li>
<li> <li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbaceGc2dlhGQnMzYXc&authuser=0">Presentations</a> <a href="https://drive.google.com/open?id=0BxgtL8yFJbaceGc2dlhGQnMzYXc&authuser=0">Presentations</a>
</li> </li>
<li> <li>
<a href="http://goo.gl/sj0Nk1">More...</a> <a href="http://goo.gl/sj0Nk1">More...</a>
</li> </li>

View File

@ -35,6 +35,7 @@ When you're done with this tutorial, the app will look like this <live-example n
Here's a visual idea of where this tutorial leads, beginning with the "Dashboard" Here's a visual idea of where this tutorial leads, beginning with the "Dashboard"
view and the most heroic heroes: view and the most heroic heroes:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -45,6 +46,7 @@ to navigate between this Dashboard view and a Heroes view.
If you click the dashboard hero "Magneta," the router opens a "Hero Details" view If you click the dashboard hero "Magneta," the router opens a "Hero Details" view
where you can change the hero's name. where you can change the hero's name.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -53,6 +55,7 @@ Clicking the "Back" button returns you to the Dashboard.
Links at the top take you to either of the main views. Links at the top take you to either of the main views.
If you click "Heroes," the app displays the "Heroes" master list view. If you click "Heroes," the app displays the "Heroes" master list view.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -64,12 +67,14 @@ editable details of the selected hero.
The following diagram captures all of the navigation options. The following diagram captures all of the navigation options.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img> <img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
</figure> </figure>
Here's the app in action: Here's the app in action:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>

View File

@ -11,19 +11,25 @@ named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>.
The file structure should look like this: The file structure should look like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
@ -32,26 +38,31 @@ The file structure should look like this:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -60,11 +71,13 @@ The file structure should look like this:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -82,6 +95,7 @@ When you're done with this page, the app should look like this <live-example></l
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -97,6 +111,7 @@ Add two properties to the `AppComponent`: a `title` property for the app name an
for a hero named "Windstorm." for a hero named "Windstorm."
<code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false">
</code-example> </code-example>
@ -104,6 +119,7 @@ for a hero named "Windstorm."
Now update the template in the `@Component` decorator with data bindings to these new properties. Now update the template in the `@Component` decorator with data bindings to these new properties.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false">
</code-example> </code-example>
@ -131,6 +147,7 @@ Create a `Hero` class with `id` and `name` properties.
Add these properties near the top of the `app.component.ts` file, just below the import statement. Add these properties near the top of the `app.component.ts` file, just below the import statement.
<code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false"> <code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false">
</code-example> </code-example>
@ -139,6 +156,7 @@ In the `Hero` class, refactor the component's `hero` property to be of type `Her
then initialize it with an `id` of `1` and the name `Windstorm`. then initialize it with an `id` of `1` and the name `Windstorm`.
<code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false"> <code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false">
</code-example> </code-example>
@ -147,6 +165,7 @@ Because you changed the hero from a string to an object,
update the binding in the template to refer to the hero's `name` property. update the binding in the template to refer to the hero's `name` property.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero-2"> <code-example path="toh-1/app/app.component.1.ts" region="show-hero-2">
</code-example> </code-example>
@ -165,6 +184,7 @@ thanks to the <i>template literals</i> feature in ES2015 and TypeScript. For mor
<code-example path="toh-1/app/app.component.1.ts" region="multi-line-strings" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="multi-line-strings" linenums="false">
</code-example> </code-example>
@ -182,6 +202,7 @@ You need a two-way binding between the `<input>` form element and the `hero.name
Refactor the hero name in the template so it looks like this: Refactor the hero name in the template so it looks like this:
<code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false">
</code-example> </code-example>
@ -207,6 +228,7 @@ of external modules that the app uses.
The updated `AppModule` looks like this: The updated `AppModule` looks like this:
<code-example path="toh-1/src/app/app.module.ts"> <code-example path="toh-1/src/app/app.module.ts">
</code-example> </code-example>
@ -242,6 +264,7 @@ Your app should look like this <live-example></live-example>.
Here's the complete `app.component.ts` as it stands now: Here's the complete `app.component.ts` as it stands now:
<code-example path="toh-1/src/app/app.component.ts"> <code-example path="toh-1/src/app/app.component.ts">
</code-example> </code-example>

View File

@ -15,19 +15,25 @@ Before you continue with this page of the Tour of Heroes,
verify that you have the following structure after [The Hero Editor](tutorial/toh-pt1) page. verify that you have the following structure after [The Hero Editor](tutorial/toh-pt1) page.
If your structure doesn't match, go back to that page to figure out what you missed. If your structure doesn't match, go back to that page to figure out what you missed.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
@ -36,26 +42,31 @@ If your structure doesn't match, go back to that page to figure out what you mis
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -64,11 +75,13 @@ If your structure doesn't match, go back to that page to figure out what you mis
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -82,6 +95,7 @@ If your structure doesn't match, go back to that page to figure out what you mis
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -99,6 +113,7 @@ To display a list of heroes, you'll add heroes to the view's template.
Create an array of ten heroes. Create an array of ten heroes.
<code-example path="toh-2/src/app/app.component.ts" region="hero-array"> <code-example path="toh-2/src/app/app.component.ts" region="hero-array">
</code-example> </code-example>
@ -112,6 +127,7 @@ Create a public property in `AppComponent` that exposes the heroes for binding.
<code-example path="toh-2/app/app.component.1.html" region="hero-array-1"> <code-example path="toh-2/app/app.component.1.html" region="hero-array-1">
</code-example> </code-example>
@ -133,6 +149,7 @@ insert the following chunk of HTML below the title and above the hero details.
<code-example path="toh-2/app/app.component.1.html" region="heroes-template-1" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="heroes-template-1" linenums="false">
</code-example> </code-example>
@ -147,6 +164,7 @@ and display them individually.
Modify the `<li>` tag by adding the built-in directive `*ngFor`. Modify the `<li>` tag by adding the built-in directive `*ngFor`.
<code-example path="toh-2/app/app.component.1.html" region="heroes-ngfor-1"> <code-example path="toh-2/app/app.component.1.html" region="heroes-ngfor-1">
</code-example> </code-example>
@ -180,6 +198,7 @@ that uses the `hero` template variable to display the hero's properties.
<code-example path="toh-2/app/app.component.1.html" region="ng-for" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="ng-for" linenums="false">
</code-example> </code-example>
@ -193,6 +212,7 @@ To add styles to your component, set the `styles` property on the `@Component` d
to the following CSS classes: to the following CSS classes:
<code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false"> <code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false">
</code-example> </code-example>
@ -208,6 +228,7 @@ The template for displaying heroes should look like this:
<code-example path="toh-2/app/app.component.1.html" region="heroes-styled" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="heroes-styled" linenums="false">
</code-example> </code-example>
@ -228,6 +249,7 @@ Add a click event binding to the `<li>` like this:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-click" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="selectedHero-click" linenums="false">
</code-example> </code-example>
@ -253,6 +275,7 @@ But the user will be able to select one of the heroes by clicking on it.
So replace the `hero` property with this simple `selectedHero` property: So replace the `hero` property with this simple `selectedHero` property:
<code-example path="toh-2/src/app/app.component.ts" region="selected-hero"> <code-example path="toh-2/src/app/app.component.ts" region="selected-hero">
</code-example> </code-example>
@ -262,6 +285,7 @@ you won't initialize the `selectedHero` as you did with `hero`.
Add an `onSelect` method that sets the `selectedHero` property to the `hero` that the user clicks. Add an `onSelect` method that sets the `selectedHero` property to the `hero` that the user clicks.
<code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false"> <code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false">
</code-example> </code-example>
@ -271,6 +295,7 @@ Bind to the new selectedHero property instead as follows:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-details" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="selectedHero-details" linenums="false">
</code-example> </code-example>
@ -281,6 +306,7 @@ When the app loads, the `selectedHero` is undefined and won't be defined until y
Angular can't display properties of the undefined `selectedHero` and throws the following error, Angular can't display properties of the undefined `selectedHero` and throws the following error,
visible in the browser's console: visible in the browser's console:
<code-example format="nocode"> <code-example format="nocode">
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null] EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
@ -293,6 +319,7 @@ Wrap the HTML hero detail content of the template with a `<div>`.
Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component. Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component.
<code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false">
</code-example> </code-example>
@ -334,12 +361,14 @@ To make the selected hero more visible, you'll apply this `selected` class to th
For example, when the user clicks "Magneta", it should render with a distinctive but subtle background color For example, when the user clicks "Magneta", it should render with a distinctive but subtle background color
like this: like this:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
In the template, add the following `[class.selected]` binding to the `<li>`: In the template, add the following `[class.selected]` binding to the `<li>`:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-1" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="class-selected-1" linenums="false">
</code-example> </code-example>
@ -358,12 +387,14 @@ Read more about the `[class]` binding in the [Template Syntax](guide/template-sy
The final version of the `<li>` looks like this: The final version of the `<li>` looks like this:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false">
</code-example> </code-example>
After clicking "Magneta", the list should look like this: After clicking "Magneta", the list should look like this:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -371,6 +402,7 @@ After clicking "Magneta", the list should look like this:
Here's the complete `app.component.ts` as of now: Here's the complete `app.component.ts` as of now:
<code-example path="toh-2/src/app/app.component.ts"> <code-example path="toh-2/src/app/app.component.ts">
</code-example> </code-example>

View File

@ -21,19 +21,25 @@ When you're done, the app should look like this <live-example></live-example>.
Before getting started on this page, verify that you have the following structure from earlier in the Tour of Heroes. Before getting started on this page, verify that you have the following structure from earlier in the Tour of Heroes.
If not, go back to the previous pages. If not, go back to the previous pages.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
@ -42,26 +48,31 @@ If not, go back to the previous pages.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -70,11 +81,13 @@ If not, go back to the previous pages.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -106,6 +119,7 @@ The `HeroDetailComponent` class goes in the `hero-detail.component.ts` file.
Start writing the `HeroDetailComponent` as follows: Start writing the `HeroDetailComponent` as follows:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -131,6 +145,7 @@ Replace the word, "selectedHero", with the word, "hero", everywhere in the templ
When you're done, the new template should look like this: When you're done, the new template should look like this:
<code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -140,6 +155,7 @@ When you're done, the new template should look like this:
The `HeroDetailComponent` template binds to the component's `hero` property. The `HeroDetailComponent` template binds to the component's `hero` property.
Add that property to the `HeroDetailComponent` class like this: Add that property to the `HeroDetailComponent` class like this:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="hero">
</code-example> </code-example>
@ -152,6 +168,7 @@ The Angular [style guide](guide/style-guide) recommends one class per file anywa
Move the `Hero` class from `app.component.ts` to its own `hero.ts` file. Move the `Hero` class from `app.component.ts` to its own `hero.ts` file.
<code-example path="toh-3/src/app/hero.ts" linenums="false"> <code-example path="toh-3/src/app/hero.ts" linenums="false">
</code-example> </code-example>
@ -159,6 +176,7 @@ Move the `Hero` class from `app.component.ts` to its own `hero.ts` file.
Now that the `Hero` class is in its own file, the `AppComponent` and the `HeroDetailComponent` have to import it. Now that the `Hero` class is in its own file, the `AppComponent` and the `HeroDetailComponent` have to import it.
Add the following `import` statement near the top of _both_ the `app.component.ts` and the `hero-detail.component.ts` files. Add the following `import` statement near the top of _both_ the `app.component.ts` and the `hero-detail.component.ts` files.
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero-import"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="hero-import">
</code-example> </code-example>
@ -170,6 +188,7 @@ the parent `AppComponent` will tell the child `HeroDetailComponent` which hero t
by binding its `selectedHero` to the `hero` property of the `HeroDetailComponent`. by binding its `selectedHero` to the `hero` property of the `HeroDetailComponent`.
The binding will look like this: The binding will look like this:
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false"> <code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example> </code-example>
@ -181,6 +200,7 @@ Otherwise, Angular rejects the binding and throws an error.
First, amend the `@angular/core` import statement to include the `Input` symbol. First, amend the `@angular/core` import statement to include the `Input` symbol.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false">
</code-example> </code-example>
@ -188,6 +208,7 @@ First, amend the `@angular/core` import statement to include the `Input` symbol.
Then declare that `hero` is an *input* property by Then declare that `hero` is an *input* property by
preceding it with the `@Input` decorator that you imported earlier. preceding it with the `@Input` decorator that you imported earlier.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="hero" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="hero" linenums="false">
</code-example> </code-example>
@ -204,6 +225,7 @@ Read more about _input_ properties in the
That's it. The `hero` property is the only thing in the `HeroDetailComponent` class. That's it. The `hero` property is the only thing in the `HeroDetailComponent` class.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -212,6 +234,7 @@ All it does is receive a hero object through its `hero` input property and then
Here's the complete `HeroDetailComponent`. Here's the complete `HeroDetailComponent`.
<code-example path="toh-3/src/app/hero-detail.component.ts"> <code-example path="toh-3/src/app/hero-detail.component.ts">
</code-example> </code-example>
@ -222,6 +245,7 @@ Every component must be declared in one&mdash;and only one&mdash;Angular module.
Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you can refer to it. Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you can refer to it.
<code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import"> <code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import">
</code-example> </code-example>
@ -229,6 +253,7 @@ Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you
Add `HeroDetailComponent` to the module's `declarations` array. Add `HeroDetailComponent` to the module's `declarations` array.
<code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false"> <code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false">
</code-example> </code-example>
@ -264,6 +289,7 @@ Coordinate the master `AppComponent` with the `HeroDetailComponent`
by binding the `selectedHero` property of the `AppComponent` by binding the `selectedHero` property of the `AppComponent`
to the `hero` property of the `HeroDetailComponent`. to the `hero` property of the `HeroDetailComponent`.
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false"> <code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example> </code-example>
@ -273,6 +299,7 @@ Now every time the `selectedHero` changes, the `HeroDetailComponent` gets a new
The revised `AppComponent` template should look like this: The revised `AppComponent` template should look like this:
<code-example path="toh-3/src/app/app.component.ts" region="hero-detail-template" linenums="false"> <code-example path="toh-3/src/app/app.component.ts" region="hero-detail-template" linenums="false">
</code-example> </code-example>
@ -297,29 +324,37 @@ without touching the parent `AppComponent`.
### Review the app structure ### Review the app structure
Verify that you have the following structure: Verify that you have the following structure:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
@ -328,26 +363,31 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -356,11 +396,13 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -374,23 +416,28 @@ Verify that you have the following structure:
Here are the code files discussed in this page. Here are the code files discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts"> <code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts"> <code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts">
</code-pane> </code-pane>

View File

@ -22,29 +22,37 @@ When you're done with this page, the app should look like this <live-example></l
Before continuing with the Tour of Heroes, verify that you have the following structure. Before continuing with the Tour of Heroes, verify that you have the following structure.
If not, go back to the previous pages. If not, go back to the previous pages.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
@ -53,26 +61,31 @@ If not, go back to the previous pages.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -81,11 +94,13 @@ If not, go back to the previous pages.
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -99,6 +114,7 @@ If not, go back to the previous pages.
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -135,6 +151,7 @@ For example, the filename for `SpecialSuperHeroService` is `special-super-hero.s
Name the class `HeroService` and export it for others to import. Name the class `HeroService` and export it for others to import.
<code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false">
</code-example> </code-example>
@ -158,6 +175,7 @@ consistency and future-proofing.
Add a `getHeroes()` method stub. Add a `getHeroes()` method stub.
<code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false">
</code-example> </code-example>
@ -173,6 +191,7 @@ Cut the `HEROES` array from `app.component.ts` and paste it to a new file in the
Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class. Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class.
<code-example path="toh-4/src/app/mock-heroes.ts"> <code-example path="toh-4/src/app/mock-heroes.ts">
</code-example> </code-example>
@ -182,6 +201,7 @@ The `HEROES` constant is exported so it can be imported elsewhere, such as the `
In `app.component.ts`, where you cut the `HEROES` array, In `app.component.ts`, where you cut the `HEROES` array,
add an uninitialized `heroes` property: add an uninitialized `heroes` property:
<code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false">
</code-example> </code-example>
@ -190,6 +210,7 @@ add an uninitialized `heroes` property:
Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method. Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method.
The `HeroService` looks like this: The `HeroService` looks like this:
<code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false">
</code-example> </code-example>
@ -199,6 +220,7 @@ You're ready to use the `HeroService` in other components, starting with `AppCom
Import the `HeroService` so that you can reference it in the code. Import the `HeroService` so that you can reference it in the code.
<code-example path="toh-4/src/app/app.component.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (hero-service-import)" region="hero-service-import"> <code-example path="toh-4/src/app/app.component.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (hero-service-import)" region="hero-service-import">
</code-example> </code-example>
@ -208,6 +230,7 @@ How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
You could create a new instance of the `HeroService` with `new` like this: You could create a new instance of the `HeroService` with `new` like this:
<code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false">
</code-example> </code-example>
@ -234,6 +257,7 @@ Instead of using the *new* line, you'll add two lines.
Add the constructor: Add the constructor:
<code-example path="toh-4/src/app/app.component.1.ts" region="ctor"> <code-example path="toh-4/src/app/app.component.1.ts" region="ctor">
</code-example> </code-example>
@ -250,6 +274,7 @@ Read more about dependency injection in the [Dependency Injection](guide/depende
The *injector* doesn't know yet how to create a `HeroService`. The *injector* doesn't know yet how to create a `HeroService`.
If you ran the code now, Angular would fail with this error: If you ran the code now, Angular would fail with this error:
<code-example format="nocode"> <code-example format="nocode">
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService) EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
</code-example> </code-example>
@ -260,6 +285,7 @@ in the `@Component` call.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (providers)" region="providers"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (providers)" region="providers">
</code-example> </code-example>
@ -273,6 +299,7 @@ The service is in a `heroService` private variable.
You could call the service and get the data in one line. You could call the service and get the data in one line.
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -280,6 +307,7 @@ You could call the service and get the data in one line.
You don't really need a dedicated method to wrap one line. Write it anyway: You don't really need a dedicated method to wrap one line. Write it anyway:
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (getHeroes)" region="getHeroes"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (getHeroes)" region="getHeroes">
</code-example> </code-example>
@ -306,11 +334,13 @@ Read more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks)
Here's the essential outline for the `OnInit` interface (don't copy this into your code): Here's the essential outline for the `OnInit` interface (don't copy this into your code):
<code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false">
</code-example> </code-example>
Add the implementation for the `OnInit` interface to your export statement: Add the implementation for the `OnInit` interface to your export statement:
<code-example format="nocode"> <code-example format="nocode">
export class AppComponent implements OnInit {} export class AppComponent implements OnInit {}
</code-example> </code-example>
@ -318,6 +348,7 @@ Add the implementation for the `OnInit` interface to your export statement:
Write an `ngOnInit` method with the initialization logic inside. Angular will call it Write an `ngOnInit` method with the initialization logic inside. Angular will call it
at the right time. In this case, initialize by calling `getHeroes()`. at the right time. In this case, initialize by calling `getHeroes()`.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (ng-on-init)" region="ng-on-init"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (ng-on-init)" region="ng-on-init">
</code-example> </code-example>
@ -328,6 +359,7 @@ when you click on a hero name.
The `HeroService` returns a list of mock heroes immediately; The `HeroService` returns a list of mock heroes immediately;
its `getHeroes()` signature is synchronous. its `getHeroes()` signature is synchronous.
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -356,6 +388,7 @@ This is a simplified explanation. Read more about ES2015 Promises in the
Update the `HeroService` with this !{_Promise}-returning `getHeroes()` method: Update the `HeroService` with this !{_Promise}-returning `getHeroes()` method:
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -367,6 +400,7 @@ by returning an *immediately resolved !{_Promise}* with the mock heroes as the r
As a result of the change to `HeroService`, `this.heroes` is now set to a !{_Promise} rather than an array of heroes. As a result of the change to `HeroService`, `this.heroes` is now set to a !{_Promise} rather than an array of heroes.
<code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false">
</code-example> </code-example>
@ -376,6 +410,7 @@ When the !{_Promise} resolves successfully, you'll have heroes to display.
Pass the callback function as an argument to the !{_Promise}'s `then()` method: Pass the callback function as an argument to the !{_Promise}'s `then()` method:
<code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -404,39 +439,49 @@ At the end of this page, [Appendix: take it slow](tutorial/toh-pt4#slow) describ
## Review the app structure ## Review the app structure
Verify that you have the following structure after all of your refactoring: Verify that you have the following structure after all of your refactoring:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
mock-heroes.ts mock-heroes.ts
</aio-file> </aio-file>
@ -445,26 +490,31 @@ Verify that you have the following structure after all of your refactoring:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -473,11 +523,13 @@ Verify that you have the following structure after all of your refactoring:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -491,18 +543,22 @@ Verify that you have the following structure after all of your refactoring:
Here are the code files discussed in this page. Here are the code files discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts"> <code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts"> <code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts">
</code-pane> </code-pane>
@ -532,6 +588,7 @@ Read about the Angular component router and navigation among the views in the [n
To simulate a slow connection, To simulate a slow connection,
import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`. import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`.
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes-slowly" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="get-heroes-slowly" linenums="false">
</code-example> </code-example>

View File

@ -15,6 +15,7 @@ There are new requirements for the Tour of Heroes app:
When youre done, users will be able to navigate the app like this: When youre done, users will be able to navigate the app like this:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img> <img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
</figure> </figure>
@ -35,39 +36,49 @@ When you're done with this page, the app should look like this <live-example></l
Before continuing with the Tour of Heroes, verify that you have the following structure. Before continuing with the Tour of Heroes, verify that you have the following structure.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
mock-heroes.ts mock-heroes.ts
</aio-file> </aio-file>
@ -76,26 +87,31 @@ Before continuing with the Tour of Heroes, verify that you have the following st
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -104,11 +120,13 @@ Before continuing with the Tour of Heroes, verify that you have the following st
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -123,6 +141,7 @@ Before continuing with the Tour of Heroes, verify that you have the following st
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -130,7 +149,6 @@ Enter the following command in the terminal window:
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes. This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes. The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser. You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
## Action plan ## Action plan
@ -165,13 +183,14 @@ move the display of *Heroes* out of `AppComponent` and into its own `HeroesCompo
### *HeroesComponent* ### *HeroesComponent*
`AppComponent` is already dedicated to *Heroes*. `AppComponent` is already dedicated to *Heroes*.
Instead of moving the code out of `AppComponent`, rename it `HeroesComponent` Instead of moving the code out of `AppComponent`, rename it to `HeroesComponent`
and create a separate `AppComponent` shell. and create a separate `AppComponent` shell.
Do the following: Do the following:
* Rename the <span ngio-ex>app.component.ts</span> file as <span ngio-ex>heroes.component.ts</span>. * Rename the <span ngio-ex>app.component.ts</span> file to <span ngio-ex>heroes.component.ts</span>.
* Rename the `AppComponent` class as `HeroesComponent` (rename locally, _only_ in this file). * Rename the `AppComponent` class to `HeroesComponent` (rename locally, _only_ in this file).
* Rename the selector `my-app` as `my-heroes`. * Rename the selector `my-app` to `my-heroes`.
<code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (showing renamings only)" region="renaming"> <code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (showing renamings only)" region="renaming">
@ -181,7 +200,7 @@ Do the following:
### Create *AppComponent* ### Create *AppComponent*
The new `AppComponent` is the application shell. The new `AppComponent` is the application shell.
It will have some navigation links at the top and a display area below for the pages users navigate to. It will have some navigation links at the top and a display area below.
Perform these steps: Perform these steps:
@ -201,13 +220,16 @@ The first draft looks like this:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts (v1)" path="toh-5/src/app/app.component.1.ts"> <code-pane title="src/app/app.component.ts (v1)" path="toh-5/src/app/app.component.1.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts (v1)" path="toh-5/src/app/app.module.1.ts"> <code-pane title="src/app/app.module.ts (v1)" path="toh-5/src/app/app.module.1.ts">
</code-pane> </code-pane>
@ -216,9 +238,9 @@ The first draft looks like this:
</code-tabs> </code-tabs>
The app still runs and displays heroes. The app still runs and displays heroes.
## Adding routing ## Add routing
Instead of displaying automatically, heroes should display after users click a button. Instead of displaying automatically, heroes should display after users click a button.
In other words, users should be able to navigate to the list of heroes. In other words, users should be able to navigate to the list of heroes.
Use the Angular router to enable navigation. Use the Angular router to enable navigation.
@ -234,6 +256,7 @@ Open `index.html` and ensure there is a `<base href="...">` element
at the top of the `<head>` section. at the top of the `<head>` section.
<code-example path="toh-pt5/src/index.html" linenums="false" title="src/index.html (base-href)" region="base-href"> <code-example path="toh-pt5/src/index.html" linenums="false" title="src/index.html (base-href)" region="base-href">
</code-example> </code-example>
@ -243,6 +266,7 @@ at the top of the `<head>` section.
~~~ {.callout.is-important} ~~~ {.callout.is-important}
<header> <header>
base href is essential base href is essential
</header> </header>
@ -267,6 +291,7 @@ pastes a URL into the browser address bar.
Define the first route as a route to the heroes component. Define the first route as a route to the heroes component.
<code-example path="toh-pt5/src/app/" linenums="false" title="src/app/ (heroes)" region="heroes"> <code-example path="toh-pt5/src/app/" linenums="false" title="src/app/ (heroes)" region="heroes">
</code-example> </code-example>
@ -306,6 +331,7 @@ Instead, add an anchor tag to the template that, when clicked, triggers navigati
The revised template looks like this: The revised template looks like this:
<code-example path="toh-pt5/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template-v2)" region="template-v2"> <code-example path="toh-pt5/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template-v2)" region="template-v2">
</code-example> </code-example>
@ -345,6 +371,7 @@ and the list of heroes displays.
`AppComponent` now looks like this: `AppComponent` now looks like this:
<code-example path="toh-pt5/src/app/app.component.1.ts" region="v2"> <code-example path="toh-pt5/src/app/app.component.1.ts" region="v2">
</code-example> </code-example>
@ -358,6 +385,7 @@ Routing only makes sense when multiple views exist.
To add another view, create a placeholder `DashboardComponent`, which users can navigate to and from. To add another view, create a placeholder `DashboardComponent`, which users can navigate to and from.
<code-example path="toh-pt5/src/app/dashboard.component.1.ts" linenums="false" title="src/app/dashboard.component.ts (v1)" region="v1"> <code-example path="toh-pt5/src/app/dashboard.component.1.ts" linenums="false" title="src/app/dashboard.component.ts (v1)" region="v1">
</code-example> </code-example>
@ -371,20 +399,22 @@ import the dashboard component and
add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions. add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions.
<code-example path="toh-pt5/_file + ' (Dashboard route)'" linenums="false" title="_file + ' (Dashboard route)' (dashboard)" region="dashboard"> <code-example path="toh-pt5/_file + ' (Dashboard route)'" linenums="false" title="_file + ' (Dashboard route)' (dashboard)" region="dashboard">
</code-example> </code-example>
### Add a redirect route ### Add a !{_redirect} route
Currently, the browser launches with `/` in the address bar.
When the app starts, it should show the dashboard and When the app starts, it should show the dashboard and
display a `/dashboard` URL in the browser address bar. display a `/dashboard` URL in the browser address bar.
Currently, the browser launches with `/` in the address bar.
To make this happen, use a redirect route. Add the following To make this happen, use a redirect route. Add the following
to the array of route definitions: to the array of route definitions:
<code-example path="toh-pt5/src/app/app.module.3.ts" linenums="false" title="src/app/app.module.ts (redirect)" region="redirect"> <code-example path="toh-pt5/src/app/app.module.3.ts" linenums="false" title="src/app/app.module.ts (redirect)" region="redirect">
</code-example> </code-example>
@ -404,6 +434,7 @@ of the [Routing & Navigation](guide/router) page.
Add a dashboard navigation link to the template, just above the *Heroes* link. Add a dashboard navigation link to the template, just above the *Heroes* link.
<code-example path="toh-pt5/src/app/app.component" linenums="false" title="src/app/app.component (template-v3)" region="template-v3"> <code-example path="toh-pt5/src/app/app.component" linenums="false" title="src/app/app.component (template-v3)" region="template-v3">
</code-example> </code-example>
@ -428,6 +459,7 @@ Replace the `template` metadata with a `templateUrl` property that points to a n
template file. template file.
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (metadata)" region="metadata"> <code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (metadata)" region="metadata">
</code-example> </code-example>
@ -436,6 +468,7 @@ template file.
Create that file with this content: Create that file with this content:
<code-example path="toh-pt5/src/app/dashboard.component.1.html"> <code-example path="toh-pt5/src/app/dashboard.component.1.html">
</code-example> </code-example>
@ -457,6 +490,7 @@ Angular injects `HeroService` and you can use it in the `DashboardComponent`.
In <span ngio-ex>dashboard.component.ts</span>, add the following `import` statements. In <span ngio-ex>dashboard.component.ts</span>, add the following `import` statements.
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (imports)" region="imports"> <code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (imports)" region="imports">
</code-example> </code-example>
@ -464,6 +498,7 @@ In <span ngio-ex>dashboard.component.ts</span>, add the following `import` state
Now create the `DashboardComponent` class like this: Now create the `DashboardComponent` class like this:
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (class)" region="class"> <code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (class)" region="class">
</code-example> </code-example>
@ -472,9 +507,9 @@ This kind of logic is also used in the `HeroesComponent`:
* Define a `heroes` !{_array} property. * Define a `heroes` !{_array} property.
* Inject the `HeroService` in the constructor and hold it in a private `!{_priv}heroService` field. * Inject the `HeroService` in the constructor and hold it in a private `!{_priv}heroService` field.
* Call the service to get heroes inside the Angular `ngOnInit` lifecycle hook. * Call the service to get heroes inside the Angular `ngOnInit()` lifecycle hook.
In this dashboard you specify four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice` method</span>. In this dashboard you specify four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice()` method</span>.
Refresh the browser to see four hero names in the new dashboard. Refresh the browser to see four hero names in the new dashboard.
@ -497,6 +532,7 @@ You didn't have to tell the `HeroesComponent` or the `DashboardComponent` anythi
Currently, the parent `HeroesComponent` sets the component's `hero` property to a Currently, the parent `HeroesComponent` sets the component's `hero` property to a
hero object with a binding like this: hero object with a binding like this:
<code-example language="html"> <code-example language="html">
&lt;hero-detail [hero]="selectedHero">&lt;/hero-detail> &lt;hero-detail [hero]="selectedHero">&lt;/hero-detail>
@ -509,6 +545,7 @@ But this binding won't work in any of the routing scenarios.
You can add the hero's `id` to the URL. When routing to the hero whose `id` is 11, You can add the hero's `id` to the URL. When routing to the hero whose `id` is 11,
you could expect to see a URL such as this: you could expect to see a URL such as this:
<code-example format="nocode"> <code-example format="nocode">
/detail/11 /detail/11
@ -522,6 +559,7 @@ You need to represent the variable part of the route with a *parameter* (or *tok
Use the following *route definition*. Use the following *route definition*.
<code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail"> <code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail">
</code-example> </code-example>
@ -542,6 +580,7 @@ is revised and ready to be navigated to.
Here's what the `HeroDetailComponent` looks like now: Here's what the `HeroDetailComponent` looks like now:
<code-example path="toh-4/src/app/hero-detail.component.ts"> <code-example path="toh-4/src/app/hero-detail.component.ts">
</code-example> </code-example>
@ -555,6 +594,7 @@ in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with
Add the following imports: Add the following imports:
<code-example path="toh-pt5/src/app/hero-detail.component" linenums="false" title="src/app/hero-detail.component (added-imports)" region="added-imports"> <code-example path="toh-pt5/src/app/hero-detail.component" linenums="false" title="src/app/hero-detail.component (added-imports)" region="added-imports">
</code-example> </code-example>
@ -563,6 +603,7 @@ Inject the `!{_ActivatedRoute}`, `HeroService`, and `Location` services
into the constructor, saving their values in private fields: into the constructor, saving their values in private fields:
<code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (constructor)" region="ctor"> <code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (constructor)" region="ctor">
</code-example> </code-example>
@ -570,16 +611,18 @@ into the constructor, saving their values in private fields:
Tell the class to implement the `OnInit` interface. Tell the class to implement the `OnInit` interface.
<code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (implement)" region="implement"> <code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (implement)" region="implement">
</code-example> </code-example>
Inside the `ngOnInit` lifecycle hook, use the `params` Observable to Inside the `ngOnInit()` lifecycle hook, use the `params` Observable to
extract the `id` parameter value from the `ActivatedRoute` service extract the `id` parameter value from the `ActivatedRoute` service
and use the `HeroService` to fetch the hero with that `id`. and use the `HeroService` to fetch the hero with that `id`.
<code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (ngOnInit)" region="ngOnInit"> <code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (ngOnInit)" region="ngOnInit">
</code-example> </code-example>
@ -592,10 +635,11 @@ If a user re-navigates to this component while a `getHero` request is still proc
`switchMap` cancels the old request and then calls `HeroService.getHero()` again. `switchMap` cancels the old request and then calls `HeroService.getHero()` again.
The hero `id` is a number. Route parameters are always strings. The hero `id` is a number. Route parameters are always strings.
So the route parameter value is converted to a number with the !{_str2int}. So the route parameter value is converted to a number with the !{_str2int}.
### Add *HeroService.getHero* ### Add *HeroService.getHero()*
In the previous code snippet, `HeroService` doesn't have a `getHero()` method. To fix this issue, In the previous code snippet, `HeroService` doesn't have a `getHero()` method. To fix this issue,
open `HeroService` and add a `getHero()` method that filters the heroes list from `getHeroes` by `id`. open `HeroService` and add a `getHero()` method that filters the heroes list from `getHeroes()` by `id`.
<code-example path="toh-pt5/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (getHero)" region="getHero"> <code-example path="toh-pt5/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (getHero)" region="getHero">
@ -611,6 +655,7 @@ Now add a third option, a `goBack()` method that navigates backward one step in
using the `Location` service you injected previously. using the `Location` service you injected previously.
<code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (goBack)" region="goBack"> <code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (goBack)" region="goBack">
</code-example> </code-example>
@ -621,7 +666,7 @@ using the `Location` service you injected previously.
Going back too far could take users out of the app. Going back too far could take users out of the app.
In a real app, you can prevent this issue with the !{_CanDeactivateGuard}. In a real app, you can prevent this issue with the !{_CanDeactivateGuard}.
Read more on the [CanDeactivate](api/!{_CanDeactivateGuardUri}) page. Read more on the [CanDeactivate](api/router/index/CanDeactivate-interface) page.
~~~ ~~~
@ -629,6 +674,7 @@ Read more on the [CanDeactivate](api/!{_CanDeactivateGuardUri}) page.
You'll wire this method with an event binding to a *Back* button that you'll add to the component template. You'll wire this method with an event binding to a *Back* button that you'll add to the component template.
<code-example path="toh-pt5/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (back-button)" region="back-button"> <code-example path="toh-pt5/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (back-button)" region="back-button">
</code-example> </code-example>
@ -637,6 +683,7 @@ Migrate the template to its own file
called <span ngio-ex>hero-detail.component.html</span>: called <span ngio-ex>hero-detail.component.html</span>:
<code-example path="toh-pt5/src/app/hero-detail.component.html"> <code-example path="toh-pt5/src/app/hero-detail.component.html">
</code-example> </code-example>
@ -645,6 +692,7 @@ Update the component metadata with a `templateUrl` pointing to the template file
<code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (metadata)" region="metadata"> <code-example path="toh-pt5/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (metadata)" region="metadata">
</code-example> </code-example>
@ -663,6 +711,7 @@ To achieve this effect, reopen `dashboard.component.html` and replace the repeat
with `<a>` tags. Change the opening `<a>` tag to the following: with `<a>` tags. Change the opening `<a>` tag to the following:
<code-example path="toh-pt5/src/app/dashboard.component.html" region="click"> <code-example path="toh-pt5/src/app/dashboard.component.html" region="click">
</code-example> </code-example>
@ -681,29 +730,12 @@ token in the parameterized hero detail route definition that you added to
`!{_appRoutingTsVsAppComp}` earlier: `!{_appRoutingTsVsAppComp}` earlier:
<code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail"> <code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail">
</code-example> </code-example>
Refresh the browser and select a hero from the dashboard; the app navigates to that heros details. Refresh the browser and select a hero from the dashboard; the app navigates to that heros details.
Here is the revised `AppModule`, compared to its pre-refactor state:
<code-tabs>
<code-pane title="src/app/app.module.ts (after)" path="toh-5/src/app/app.module.ts">
</code-pane>
<code-pane title="src/app/app.module.ts (before)" path="toh-5/src/app/app.module.3.ts">
</code-pane>
</code-tabs>
The revised and simplified `AppModule` is focused on identifying the key pieces of the app.
## Select a hero in the *HeroesComponent* ## Select a hero in the *HeroesComponent*
@ -712,6 +744,7 @@ the current template exhibits a "master/detail" style with the list of heroes
at the top and details of the selected hero below. at the top and details of the selected hero below.
<code-example path="toh-4/src/app/app.component.ts" region="template" linenums="false"> <code-example path="toh-4/src/app/app.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -731,12 +764,14 @@ Instead, they'll see a mini detail on *this* page and have to click a button to
Add the following HTML fragment at the bottom of the template where the `<hero-detail>` used to be: Add the following HTML fragment at the bottom of the template where the `<hero-detail>` used to be:
<code-example path="toh-pt5/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (mini-detail)" region="mini-detail"> <code-example path="toh-pt5/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (mini-detail)" region="mini-detail">
</code-example> </code-example>
After clicking a hero, users should see something like this below the hero list: After clicking a hero, users should see something like this below the hero list:
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -747,6 +782,7 @@ The hero's name is displayed in capital letters because of the `uppercase` pipe
that's included in the interpolation binding, right after the pipe operator ( | ). that's included in the interpolation binding, right after the pipe operator ( | ).
<code-example path="toh-pt5/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (pipe)" region="pipe"> <code-example path="toh-pt5/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (pipe)" region="pipe">
</code-example> </code-example>
@ -781,13 +817,16 @@ styles contents into a new <span ngio-ex>heroes.component.css</span> file.
The two new files should look like this: The two new files should look like this:
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes.component.html" path="toh-5/src/app/heroes.component.html"> <code-pane title="src/app/heroes.component.html" path="toh-5/src/app/heroes.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/heroes.component.css" path="toh-5/src/app/heroes.component.css"> <code-pane title="src/app/heroes.component.css" path="toh-5/src/app/heroes.component.css">
</code-pane> </code-pane>
@ -802,6 +841,7 @@ Set their properties to refer to the new files.
<code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (revised metadata)" region="metadata"> <code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (revised metadata)" region="metadata">
</code-example> </code-example>
@ -826,7 +866,8 @@ This approach requires the following changes to the component class:
1. Import the `router` from the Angular router library. 1. Import the `router` from the Angular router library.
1. Inject the `router` in the constructor, along with the `HeroService`. 1. Inject the `router` in the constructor, along with the `HeroService`.
1. Implement `gotoDetail()` by calling the `router.navigate()` method. 1. Implement `gotoDetail()` by calling the router `navigate()` method.
<code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (gotoDetail)" region="gotoDetail"> <code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (gotoDetail)" region="gotoDetail">
@ -834,12 +875,13 @@ This approach requires the following changes to the component class:
</code-example> </code-example>
Note that you're passing a two-element *link parameters !{_array}*&mdash;a Note that you're passing a two-element *link parameters !{_array}*&mdash;a
path and the route parameter&mdash;to !{_pathVsName} and the route parameter&mdash;to
the `router.navigate()` method, just as you did in the `[routerLink]` binding the router `navigate()` method, just as you did in the `[routerLink]` binding
back in the `DashboardComponent`. back in the `DashboardComponent`.
Here's the revised `HeroesComponent` class: Here's the revised `HeroesComponent` class:
<code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (class)" region="class"> <code-example path="toh-pt5/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (class)" region="class">
</code-example> </code-example>
@ -864,6 +906,7 @@ Add a <span ngio-ex>dashboard.component.css</span> file to the `!{_appDir}` fold
that file in the component metadata's `styleUrls` !{_array} property like this: that file in the component metadata's `styleUrls` !{_array} property like this:
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (styleUrls)" region="css"> <code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (styleUrls)" region="css">
</code-example> </code-example>
@ -884,11 +927,13 @@ Here's the content for the component CSS files.
<code-tabs> <code-tabs>
<code-pane title="src/app/hero-detail.component.css" path="toh-5/src/app/hero-detail.component.css"> <code-pane title="src/app/hero-detail.component.css" path="toh-5/src/app/hero-detail.component.css">
</code-pane> </code-pane>
<code-pane title="src/app/dashboard.component.css" path="toh-5/src/app/dashboard.component.css"> <code-pane title="src/app/dashboard.component.css" path="toh-5/src/app/dashboard.component.css">
</code-pane> </code-pane>
@ -904,6 +949,7 @@ You'll surround those links in `<nav>` tags.
Add an <span ngio-ex>app.component.css</span> file to the `!{_appDir}` folder with the following content. Add an <span ngio-ex>app.component.css</span> file to the `!{_appDir}` folder with the following content.
<code-example path="toh-pt5/src/app/app.component.css" linenums="false" title="src/app/app.component.css (navigation styles)" region="navigation styles"> <code-example path="toh-pt5/src/app/app.component.css" linenums="false" title="src/app/app.component.css (navigation styles)" region="navigation styles">
</code-example> </code-example>
@ -932,6 +978,7 @@ All you have to do is define the style for it.
Add a `styleUrls` property that refers to this CSS file as follows: Add a `styleUrls` property that refers to this CSS file as follows:
<code-example path="toh-pt5/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (styleUrls)" region="styleUrls"> <code-example path="toh-pt5/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (styleUrls)" region="styleUrls">
</code-example> </code-example>
@ -949,6 +996,7 @@ These correspond to the full set of master styles that you installed earlier dur
Here's an excerpt: Here's an excerpt:
<code-example path="toh-pt5/src/styles.css" linenums="false" title="src/styles.css (excerpt)" region="toh"> <code-example path="toh-pt5/src/styles.css" linenums="false" title="src/styles.css (excerpt)" region="toh">
</code-example> </code-example>
@ -958,12 +1006,14 @@ Ensure that the file contains the [master styles provided here](tutorial/!{style
Also edit <span ngio-ex>index.html</span> to refer to this stylesheet. Also edit <span ngio-ex>index.html</span> to refer to this stylesheet.
<code-example path="toh-pt5/src/index.html" linenums="false" title="src/index.html (link ref)" region="css"> <code-example path="toh-pt5/src/index.html" linenums="false" title="src/index.html (link ref)" region="css">
</code-example> </code-example>
Look at the app now. The dashboard, heroes, and navigation links are styled. Look at the app now. The dashboard, heroes, and navigation links are styled.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -975,89 +1025,109 @@ Review the sample source code in the <live-example></live-example> for this page
Verify that you have the following structure: Verify that you have the following structure:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.css app.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app-routing.module.ts app-routing.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.css dashboard.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.html dashboard.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.ts dashboard.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.css hero-detail.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.html hero-detail.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.css heroes.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.html heroes.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.ts heroes.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
mock-heroes.ts mock-heroes.ts
</aio-file> </aio-file>
@ -1066,26 +1136,31 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -1094,11 +1169,13 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>

View File

@ -24,6 +24,7 @@ That's the starting point for this page.
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm start npm start
@ -31,10 +32,10 @@ Enter the following command in the terminal window:
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes. This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes. The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser. You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
<h1> <h1>
Providing HTTP Services Providing HTTP Services
</h1> </h1>
@ -45,7 +46,7 @@ The `HttpModule` is not a core Angular module.
and is shipped in a separate script file as part of the Angular npm package. and is shipped in a separate script file as part of the Angular npm package.
You're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when you need it. You're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when you need it.
### Register for HTTP services ## Register for HTTP services
The app will depend on the Angular `http` service, which itself depends on other supporting services. The app will depend on the Angular `http` service, which itself depends on other supporting services.
The `HttpModule` from the `@angular/http` library holds providers for a complete set of HTTP services. The `HttpModule` from the `@angular/http` library holds providers for a complete set of HTTP services.
@ -54,6 +55,7 @@ To allow access to these services from anywhere in the app,
add `HttpModule` to the `imports` list of the `AppModule`. add `HttpModule` to the `imports` list of the `AppModule`.
<code-example path="toh-pt6/src/app/app.module.ts" region="v1"> <code-example path="toh-pt6/src/app/app.module.ts" region="v1">
</code-example> </code-example>
@ -62,20 +64,14 @@ Notice that you also supply `!{_HttpModule}` as part of the *imports* !{_array}
## Simulate the web API ## Simulate the web API
We recommend registering app-wide services in the root
`!{_AppModuleVsAppComp}` *providers*. <span if-docs="dart">Here you're
registering in `main` for a special reason.</span>
Until you have a web server that can handle requests for hero data, Until you have a web server that can handle requests for hero data,
the HTTP client will fetch and save data from the HTTP client will fetch and save data from
a mock service, the *in-memory web API*. a mock service, the *in-memory web API*.
<span if-docs="dart"> The app itself doesn't need to know
about this, so you can slip the in-memory web API into the
configuration above the `AppComponent`.</span>
Update <span ngio-ex>!{_appModuleTsVsMainTs}</span> with this version, which uses the mock service: Update <span ngio-ex>!{_appModuleTsVsMainTs}</span> with this version, which uses the mock service:
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (v2)" region="v2"> <code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (v2)" region="v2">
</code-example> </code-example>
@ -86,6 +82,7 @@ Rather than require a real API server, this example simulates communication with
to the module `imports`, effectively replacing the `Http` client's XHR backend service with an in-memory alternative. to the module `imports`, effectively replacing the `Http` client's XHR backend service with an in-memory alternative.
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (in-mem-web-api)" region="in-mem-web-api"> <code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (in-mem-web-api)" region="in-mem-web-api">
</code-example> </code-example>
@ -95,6 +92,7 @@ that primes the in-memory database.
Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content: Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content:
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" linenums="false"> <code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" linenums="false">
</code-example> </code-example>
@ -122,6 +120,7 @@ section of the [HTTP Client](guide/server-communication) page.
In the current `HeroService` implementation, a !{_Promise} resolved with mock heroes is returned. In the current `HeroService` implementation, a !{_Promise} resolved with mock heroes is returned.
<code-example path="toh-4/src/app/hero.service.ts" linenums="false" title="toh-4/ts/src/app/hero.service.ts (old getHeroes)" region="get-heroes"> <code-example path="toh-4/src/app/hero.service.ts" linenums="false" title="toh-4/ts/src/app/hero.service.ts (old getHeroes)" region="get-heroes">
</code-example> </code-example>
@ -132,6 +131,7 @@ fetching heroes with an HTTP client, which must be an asynchronous operation.
Now convert `getHeroes()` to use HTTP. Now convert `getHeroes()` to use HTTP.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated getHeroes and new class members)" region="getHeroes"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated getHeroes and new class members)" region="getHeroes">
</code-example> </code-example>
@ -139,6 +139,7 @@ Now convert `getHeroes()` to use HTTP.
Update the import statements as follows: Update the import statements as follows:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated imports)" region="imports"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated imports)" region="imports">
</code-example> </code-example>
@ -156,6 +157,7 @@ You'll read about [Observables](tutorial/toh-pt6#observables) later in this page
For now, you've converted the `Observable` to a `Promise` using the `toPromise` operator. For now, you've converted the `Observable` to a `Promise` using the `toPromise` operator.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-promise)" region="to-promise"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-promise)" region="to-promise">
</code-example> </code-example>
@ -167,6 +169,7 @@ To use those capabilities, you have to add the operators themselves.
That's as easy as importing them from the RxJS library like this: That's as easy as importing them from the RxJS library like this:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (rxjs)" region="rxjs"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (rxjs)" region="rxjs">
</code-example> </code-example>
@ -186,6 +189,7 @@ In the *Promise*'s `then()` callback, you call the `json` method of the HTTP `Re
data within the response. data within the response.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-data)" region="to-data"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-data)" region="to-data">
</code-example> </code-example>
@ -212,6 +216,7 @@ It receives a !{_Promise} of *heroes* just as it did before.
At the end of `getHeroes()`, you `catch` server failures and pass them to an error handler. At the end of `getHeroes()`, you `catch` server failures and pass them to an error handler.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (catch)" region="catch"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (catch)" region="catch">
</code-example> </code-example>
@ -220,11 +225,12 @@ This is a critical step.
You must anticipate HTTP failures, as they happen frequently for reasons beyond your control. You must anticipate HTTP failures, as they happen frequently for reasons beyond your control.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (handleError)" region="handleError"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (handleError)" region="handleError">
</code-example> </code-example>
In this demo service, you log the error to the console; in real life, This demo service logs the error to the console; in real life,
you would handle the error in code. For a demo, this works. you would handle the error in code. For a demo, this works.
The code also includes an error to The code also includes an error to
@ -239,13 +245,14 @@ filters for the one with the matching `id`.
That's fine for a simulation, but it's wasteful to ask a real server for all heroes when you only want one. That's fine for a simulation, but it's wasteful to ask a real server for all heroes when you only want one.
Most web APIs support a _get-by-id_ request in the form `api/hero/:id` (such as `api/hero/11`). Most web APIs support a _get-by-id_ request in the form `api/hero/:id` (such as `api/hero/11`).
Update the `HeroService.getHero` method to make a _get-by-id_ request: Update the `HeroService.getHero()` method to make a _get-by-id_ request:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (getHero)" region="getHero"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (getHero)" region="getHero">
</code-example> </code-example>
This request is almost the same as `getHeroes`. This request is almost the same as `getHeroes()`.
The hero id in the URL identifies which hero the server should update. The hero id in the URL identifies which hero the server should update.
Also, the `data` in the response is a single hero object rather than !{_an} !{_array}. Also, the `data` in the response is a single hero object rather than !{_an} !{_array}.
@ -274,25 +281,28 @@ the server.
### Add the ability to save hero details ### Add the ability to save hero details
At the end of the hero detail template, add a save button with a `click` event At the end of the hero detail template, add a save button with a `click` event
binding that invokes a new component method named `save`. binding that invokes a new component method named `save()`.
<code-example path="toh-pt6/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (save)" region="save"> <code-example path="toh-pt6/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (save)" region="save">
</code-example> </code-example>
Add the following `save` method, which persists hero name changes using the hero service Add the following `save()` method, which persists hero name changes using the hero service
`update` method and then navigates back to the previous view. `update()` method and then navigates back to the previous view.
<code-example path="toh-pt6/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (save)" region="save"> <code-example path="toh-pt6/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (save)" region="save">
</code-example> </code-example>
### Add a hero service _update_ method ### Add a hero service _update()_ method
The overall structure of the `update()` method is similar to that of
`getHeroes()`, but it uses an HTTP `put()` to persist server-side changes.
The overall structure of the `update` method is similar to that of
`getHeroes`, but it uses an HTTP `put()` to persist server-side changes.
@ -317,6 +327,7 @@ Insert the following into the heroes component HTML, just after
the heading: the heading:
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (add)" region="add"> <code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (add)" region="add">
</code-example> </code-example>
@ -325,6 +336,7 @@ In response to a click event, call the component's click handler and then
clear the input field so that it's ready for another name. clear the input field so that it's ready for another name.
<code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (add)" region="add"> <code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (add)" region="add">
</code-example> </code-example>
@ -332,7 +344,8 @@ clear the input field so that it's ready for another name.
When the given name is non-blank, the handler delegates creation of the When the given name is non-blank, the handler delegates creation of the
named hero to the hero service, and then adds the new hero to the !{_array}. named hero to the hero service, and then adds the new hero to the !{_array}.
Implement the `create` method in the `HeroService` class. Implement the `create()` method in the `HeroService` class.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (create)" region="create"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (create)" region="create">
@ -345,7 +358,8 @@ Refresh the browser and create some heroes.
Each hero in the heroes view should have a delete button. Each hero in the heroes view should have a delete button.
Add the following button element to the heroes component HTML, after the hero Add the following button element to the heroes component HTML, after the hero
name in the repeated `<li>` tag. name in the repeated `<li>` element.
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (delete)" region="delete"> <code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (delete)" region="delete">
@ -355,6 +369,7 @@ name in the repeated `<li>` tag.
The `<li>` element should now look like this: The `<li>` element should now look like this:
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (li-element)" region="li-element"> <code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (li-element)" region="li-element">
</code-example> </code-example>
@ -367,6 +382,7 @@ select the hero that the user will delete.
The logic of the `delete()` handler is a bit trickier: The logic of the `delete()` handler is a bit trickier:
<code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (delete)" region="delete"> <code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (delete)" region="delete">
</code-example> </code-example>
@ -375,24 +391,27 @@ Of course you delegate hero deletion to the hero service, but the component
is still responsible for updating the display: it removes the deleted hero is still responsible for updating the display: it removes the deleted hero
from the !{_array} and resets the selected hero, if necessary. from the !{_array} and resets the selected hero, if necessary.
To place the delete button at the far right of the hero entry, To place the delete button at the far right of the hero entry,
add this additional CSS: add this CSS:
<code-example path="toh-pt6/src/app/heroes.component.css" linenums="false" title="src/app/heroes.component.css (additions)" region="additions"> <code-example path="toh-pt6/src/app/heroes.component.css" linenums="false" title="src/app/heroes.component.css (additions)" region="additions">
</code-example> </code-example>
### Hero service `delete()` method ### Hero service _delete()_ method
Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server: Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (delete)" region="delete"> <code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (delete)" region="delete">
</code-example> </code-example>
Refresh the browser and try the new delete functionality. Refresh the browser and try the new delete functionality.
<div id='observables'> <div id='observables'>
</div> </div>
@ -421,8 +440,8 @@ The calling component can easily consume a single result in the form of a Promis
But requests aren't always done only once. But requests aren't always done only once.
You may start one request, You may start one request,
cancel it, and make a different request before the server has responded to the first request. cancel it, and make a different request before the server has responded to the first request.
A *request-cancel-new-request* sequence is difficult to implement with *!{Promise}s*, but A *request-cancel-new-request* sequence is difficult to implement with *!{_Promise}s*, but
easy with *!{Observable}s*. easy with *!{_Observable}s*.
### Add the ability to search by name ### Add the ability to search by name
You're going to add a *hero search* feature to the Tour of Heroes. You're going to add a *hero search* feature to the Tour of Heroes.
@ -431,6 +450,7 @@ As the user types a name into a search box, you'll make repeated HTTP requests f
Start by creating `HeroSearchService` that sends search queries to the server's web API. Start by creating `HeroSearchService` that sends search queries to the server's web API.
<code-example path="toh-pt6/src/app/hero-search.service.ts"> <code-example path="toh-pt6/src/app/hero-search.service.ts">
</code-example> </code-example>
@ -442,9 +462,8 @@ in the `HeroService`, although the URL now has a query string.
Instead you return the *Observable* from the the `htttp.get()`, Instead you return the *Observable* from the the `htttp.get()`,
after chaining it to another RxJS operator, <code>map()</code>, after chaining it to another RxJS operator, <code>map()</code>,
to extract heroes from the response data. to extract heroes from the response data.
RxJS operator chaining makes response processing easy and readable. RxJS operator chaining makes response processing easy and readable.
See the [discussion below about operators](tutorial/toh-pt6#rxjs-imports). See the [discussion below about operators](tutorial/toh-pt6#rxjs-imports).</span>
### HeroSearchComponent ### HeroSearchComponent
Create a `HeroSearchComponent` that calls the new `HeroSearchService`. Create a `HeroSearchComponent` that calls the new `HeroSearchService`.
@ -452,12 +471,14 @@ Create a `HeroSearchComponent` that calls the new `HeroSearchService`.
The component template is simple&mdash;just a text box and a list of matching search results. The component template is simple&mdash;just a text box and a list of matching search results.
<code-example path="toh-pt6/src/app/hero-search.component.html"> <code-example path="toh-pt6/src/app/hero-search.component.html">
</code-example> </code-example>
Also, add styles for the new component. Also, add styles for the new component.
<code-example path="toh-pt6/src/app/hero-search.component.css"> <code-example path="toh-pt6/src/app/hero-search.component.css">
</code-example> </code-example>
@ -474,13 +495,15 @@ The `async` pipe subscribes to the `!{_Observable}` and produces the !{_array} o
Create the `HeroSearchComponent` class and metadata. Create the `HeroSearchComponent` class and metadata.
<code-example path="toh-pt6/src/app/hero-search.component.ts"> <code-example path="toh-pt6/src/app/hero-search.component.ts">
</code-example> </code-example>
#### Search terms #### Search terms
Focus on the `!{_priv}searchTerms`: Focus on `!{_priv}searchTerms`:
<code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (searchTerms)" region="searchTerms"> <code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (searchTerms)" region="searchTerms">
@ -500,6 +523,7 @@ You can turn the stream
of search terms into a stream of `Hero` !{_array}s and assign the result to the `heroes` property. of search terms into a stream of `Hero` !{_array}s and assign the result to the `heroes` property.
<code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (search)" region="search"> <code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (search)" region="search">
</code-example> </code-example>
@ -512,8 +536,8 @@ You'll make fewer calls to the `HeroSearchService` and still get timely results.
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds * `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
before passing along the latest string. You'll never make requests more frequently than 300ms. before passing along the latest string. You'll never make requests more frequently than 300ms.
* `distinctUntilChanged` ensures that a request is sent only if the filter text changed. * `distinctUntilChanged()` ensures that a request is sent only if the filter text changed.
* `switchMap()` calls the search service for each search term that makes it through `debounce` and `distinctUntilChanged`. * `switchMap()` calls the search service for each search term that makes it through `debounceTime()` and `distinctUntilChanged()`.
It cancels and discards previous search observables, returning only the latest search service observable. It cancels and discards previous search observables, returning only the latest search service observable.
@ -553,6 +577,7 @@ When you need more RxJS features, extend `Observable` by *importing* the librar
Here are all the RxJS imports that _this_ component needs: Here are all the RxJS imports that _this_ component needs:
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" linenums="false"> <code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" linenums="false">
</code-example> </code-example>
@ -568,6 +593,7 @@ loads and executes the library's script file which, in turn, adds the operator t
Add the hero search HTML element to the bottom of the `DashboardComponent` template. Add the hero search HTML element to the bottom of the `DashboardComponent` template.
<code-example path="toh-pt6/src/app/dashboard.component.html" linenums="false"> <code-example path="toh-pt6/src/app/dashboard.component.html" linenums="false">
</code-example> </code-example>
@ -577,6 +603,7 @@ Finally, import `HeroSearchComponent` from
and add it to the `!{_declarations}` !{_array}. and add it to the `!{_declarations}` !{_array}.
<code-example path="toh-pt6/declFile" linenums="false" title="declFile (search)" region="search"> <code-example path="toh-pt6/declFile" linenums="false" title="declFile (search)" region="search">
</code-example> </code-example>
@ -584,6 +611,7 @@ and add it to the `!{_declarations}` !{_array}.
Run the app again. In the Dashboard, enter some text in the search box. Run the app again. In the Dashboard, enter some text in the search box.
If you enter characters that match any existing hero names, you'll see something like this. If you enter characters that match any existing hero names, you'll see something like this.
<figure class='image-display'> <figure class='image-display'>
<img src='assets/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> </figure>
@ -595,109 +623,133 @@ Review the sample source code in the <live-example></live-example> for this page
Verify that you have the following structure: Verify that you have the following structure:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.css app.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app-routing.module.ts app-routing.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.css dashboard.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.html dashboard.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.ts dashboard.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.css hero-detail.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.html hero-detail.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.html (new) hero-search.component.html (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.css (new) hero-search.component.css (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.ts (new) hero-search.component.ts (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.service.ts (new) hero-search.service.ts (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.css heroes.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.html heroes.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.ts heroes.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
in-memory-data.service.ts (new) in-memory-data.service.ts (new)
</aio-file> </aio-file>
@ -706,26 +758,31 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
@ -734,11 +791,13 @@ Verify that you have the following structure:
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
@ -764,48 +823,58 @@ Here are the files you added or changed in this page.
<code-tabs> <code-tabs>
<code-pane title="app.comp...ts" path="toh-6/src/app/app.component.ts"> <code-pane title="app.comp...ts" path="toh-6/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app.mod...ts" path="toh-6/src/app/app.module.ts"> <code-pane title="app.mod...ts" path="toh-6/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="heroes.comp...ts" path="toh-6/src/app/heroes.component.ts"> <code-pane title="heroes.comp...ts" path="toh-6/src/app/heroes.component.ts">
</code-pane> </code-pane>
<code-pane title="heroes.comp...html" path="toh-6/src/app/heroes.component.html"> <code-pane title="heroes.comp...html" path="toh-6/src/app/heroes.component.html">
</code-pane> </code-pane>
<code-pane title="heroes.comp...css" path="toh-6/src/app/heroes.component.css"> <code-pane title="heroes.comp...css" path="toh-6/src/app/heroes.component.css">
</code-pane> </code-pane>
<code-pane title="hero-detail.comp...ts" path="toh-6/src/app/hero-detail.component.ts"> <code-pane title="hero-detail.comp...ts" path="toh-6/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="hero-detail.comp...html" path="toh-6/src/app/hero-detail.component.html"> <code-pane title="hero-detail.comp...html" path="toh-6/src/app/hero-detail.component.html">
</code-pane> </code-pane>
<code-pane title="hero.service.ts" path="toh-6/src/app/hero.service.ts"> <code-pane title="hero.service.ts" path="toh-6/src/app/hero.service.ts">
</code-pane> </code-pane>
<code-pane title="in-memory-data.service.ts" path="toh-6/src/app/in-memory-data.service.ts"> <code-pane title="in-memory-data.service.ts" path="toh-6/src/app/in-memory-data.service.ts">
</code-pane> </code-pane>
@ -815,23 +884,28 @@ Here are the files you added or changed in this page.
<code-tabs> <code-tabs>
<code-pane title="hero-search.service.ts" path="toh-6/src/app/hero-search.service.ts"> <code-pane title="hero-search.service.ts" path="toh-6/src/app/hero-search.service.ts">
</code-pane> </code-pane>
<code-pane title="hero-search.component.ts" path="toh-6/src/app/hero-search.component.ts"> <code-pane title="hero-search.component.ts" path="toh-6/src/app/hero-search.component.ts">
</code-pane> </code-pane>
<code-pane title="hero-search.component.html" path="toh-6/src/app/hero-search.component.html"> <code-pane title="hero-search.component.html" path="toh-6/src/app/hero-search.component.html">
</code-pane> </code-pane>
<code-pane title="hero-search.component.css" path="toh-6/src/app/hero-search.component.css"> <code-pane title="hero-search.component.css" path="toh-6/src/app/hero-search.component.css">
</code-pane> </code-pane>