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>
<h2 id='devenv'>
Step 1. Set up the Development Environment
</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.
<code-example language="sh" class="code-shell">
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'>
Step 2. Create a new project
</h2>
@ -57,6 +60,7 @@ Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globa
Open a terminal window.
Generate a new project and skeleton application by running the following commands:
<code-example language="sh" class="code-shell">
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'>
Step 3: Serve the application
</h2>
Go to the project directory and launch the server.
<code-example language="sh" class="code-shell">
cd my-app
ng serve --open
@ -94,12 +100,14 @@ on `http://localhost:4200/`.
Your app greets you with a message:
<figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/app-works.png' alt="The app works!"> </img>
</figure>
<h2 id='first-component'>
Step 4: Edit your first Angular component
</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_:
<code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false">
</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.
<code-example path="cli-quickstart/src/app/app.component.css" linenums="false">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"> </img>
</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.
Any files outside of this folder are meant to support building your app.
<aio-filetree>
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.css
</aio-file>
<aio-file>
app.component.html
</aio-file>
<aio-file>
app.component.spec.ts
</aio-file>
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
@ -189,8 +208,10 @@ Any files outside of this folder are meant to support building your app.
</aio-folder>
<aio-folder>
assets
<aio-file>
.gitkeep
</aio-file>
@ -199,13 +220,16 @@ Any files outside of this folder are meant to support building your app.
</aio-folder>
<aio-folder>
environments
<aio-file>
environment.prod.ts
</aio-file>
<aio-file>
environment.ts
</aio-file>
@ -214,41 +238,49 @@ Any files outside of this folder are meant to support building your app.
</aio-folder>
<aio-file>
favicon.ico
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
main.ts
</aio-file>
<aio-file>
polyfills.ts
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
test.ts
</aio-file>
<aio-file>
tsconfig.app.json
</aio-file>
<aio-file>
tsconfig.spec.json
</aio-file>
@ -260,30 +292,37 @@ Any files outside of this folder are meant to support building your app.
</aio-filetree>
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="20%">
</col>
<col width="80%">
</col>
<tr>
<th>
File
</th>
<th>
Purpose
</th>
@ -292,13 +331,16 @@ Any files outside of this folder are meant to support building your app.
</tr>
<tr>
<td>
<code>app/app.component.{ts,html,css,spec.ts}</code>
</td>
<td>
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
@ -309,13 +351,16 @@ Any files outside of this folder are meant to support building your app.
</tr>
<tr>
<td>
<code>app/app.module.ts</code>
</td>
<td>
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
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>
<td>
<code>assets/*</code>
</td>
<td>
A folder where you can put images and anything else to be copied wholesale
when you build your application.
@ -342,13 +390,16 @@ Any files outside of this folder are meant to support building your app.
</tr>
<tr>
<td>
<code>environments/*</code>
</td>
<td>
This folder contains one file for each of your destination environments,
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>
<td>
<code>favicon.ico</code>
</td>
<td>
Every site wants to look good on the bookmark bar.
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>
<td>
<code>index.html</code>
</td>
<td>
The main HTML page that is served when someone visits your site.
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>
<td>
<code>main.ts</code>
</td>
<td>
The main entry point for your app.
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>
<td>
<code>polyfills.ts</code>
</td>
<td>
Different browsers have different levels of support of the web standards.
Polyfills help normalize those differences.
@ -434,13 +497,16 @@ Any files outside of this folder are meant to support building your app.
</tr>
<tr>
<td>
<code>styles.css</code>
</td>
<td>
Your global styles go here.
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>
<td>
<code>test.ts</code>
</td>
<td>
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
@ -468,13 +537,16 @@ Any files outside of this folder are meant to support building your app.
</tr>
<tr>
<td>
<code>tsconfig.{app|spec}.json</code>
</td>
<td>
TypeScript compiler configuration for the Angular app (`tsconfig.app.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.
These files go in the root folder next to `src/`.
<aio-filetree>
<aio-folder>
my-app
<aio-folder>
e2e
<aio-file>
app.e2e-spec.ts
</aio-file>
<aio-file>
app.po.ts
</aio-file>
<aio-file>
tsconfig.e2e.json
</aio-file>
@ -518,56 +596,67 @@ These files go in the root folder next to `src/`.
</aio-folder>
<aio-file>
node_modules/...
</aio-file>
<aio-file>
src/...
</aio-file>
<aio-file>
.angular-cli.json
</aio-file>
<aio-file>
.editorconfig
</aio-file>
<aio-file>
.gitignore
</aio-file>
<aio-file>
karma.conf.js
</aio-file>
<aio-file>
package.json
</aio-file>
<aio-file>
protractor.conf.js
</aio-file>
<aio-file>
README.md
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
<aio-file>
tslint.json
</aio-file>
@ -579,30 +668,37 @@ These files go in the root folder next to `src/`.
</aio-filetree>
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="20%">
</col>
<col width="80%">
</col>
<tr>
<th>
File
</th>
<th>
Purpose
</th>
@ -611,13 +707,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>e2e/</code>
</td>
<td>
Inside `e2e/` live the End-to-End tests.
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>
<td>
<code>node_modules/</code>
</td>
<td>
`Node.js` creates this folder and puts all third party modules listed in
`package.json` inside of it.
@ -645,13 +747,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>.angular-cli.json</code>
</td>
<td>
Configuration for Angular CLI.
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>
<td>
<code>.editorconfig</code>
</td>
<td>
Simple configuration for your editor to make sure everyone that uses your project
has the same basic configuration.
@ -681,13 +789,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>.gitignore</code>
</td>
<td>
Git configuration to make sure autogenerated files are not commited to source control.
</td>
@ -696,13 +807,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>karma.conf.js</code>
</td>
<td>
Unit test configuration for the [Karma test runner](https://karma-runner.github.io),
used when running `ng test`.
@ -712,13 +826,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>package.json</code>
</td>
<td>
`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.
@ -728,13 +845,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>protractor.conf.js</code>
</td>
<td>
End-to-end test configuration for [Protractor](http://www.protractortest.org/),
used when running `ng e2e`.
@ -744,13 +864,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>README.md</code>
</td>
<td>
Basic documentation for your project, pre-filled with CLI command information.
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>
<td>
<code>tsconfig.json</code>
</td>
<td>
TypeScript compiler configuration for your IDE to pick up and give you helpful tooling.
</td>
@ -776,13 +902,16 @@ These files go in the root folder next to `src/`.
</tr>
<tr>
<td>
<code>tslint.json</code>
</td>
<td>
Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with
[Codelyzer](http://codelyzer.com/), used when running `ng lint`.

View File

@ -1,4 +1,4 @@
# _boilerplate files
# boilerplate files
**/src/styles.css
**/src/systemjs-angular-loader.js
**/src/systemjs.config.js
@ -23,12 +23,18 @@ protractor-helpers.js
dist/
# special
!_boilerplate/
!/*
!systemjs.config.*.js
!*.1.*
!*.2.*
!*.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
!**/*.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.
~~~
## 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
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}
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.
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.
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).
~~~
{@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
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.
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
HTML itself.
Directives fall into one of the following categories:
* [Components](glossary#component) combine application logic with an HTML template to
render application [views](glossary#view). Components are usually represented as HTML elements.
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
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
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
or displayed between element tags, as in this example.
<code-example language="html" escape="html">
<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}
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
a numeric value in the local currency.
<code-example language="html" escape="html">
<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}
## 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
~~~ {.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
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
@ -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
~~~ {.l-sub-section}
@ -579,10 +684,8 @@ For more information, see the [Services](!{docsLatest}/tutorial/toh-pt4) page of
{@a snake-case}
## snake_case
~~~ {.l-sub-section}
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*.
@ -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.
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).
~~~
## 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}
A mechanism for encapsulating and intercepting
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.
The following table lists some of the key AngularJS template features with their equivalent Angular template syntax.
<table width="100%">
<col width="50%">
</col>
<col width="50%">
</col>
<tr>
<th>
AngularJS
</th>
<th>
Angular
</th>
@ -58,10 +64,13 @@ The following table lists some of the key AngularJS template features with their
</tr>
<tr style=top>
<td>
### Bindings/interpolation
<code-example>
Your favorite hero is: {{vm.favoriteHero}}
</code-example>
@ -76,9 +85,11 @@ The following table lists some of the key AngularJS template features with their
</td>
<td>
### Bindings/interpolation
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false">
</code-example>
@ -96,10 +107,13 @@ The following table lists some of the key AngularJS template features with their
</tr>
<tr style=top>
<td>
### Filters
<code-example>
&lt;td>{{movie.title | uppercase}}&lt;/td>
</code-example>
@ -110,9 +124,11 @@ The following table lists some of the key AngularJS template features with their
</td>
<td>
### Pipes
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false">
</code-example>
@ -128,10 +144,13 @@ The following table lists some of the key AngularJS template features with their
</tr>
<tr style=top>
<td>
### Local variables
<code-example format="">
&lt;tr ng-repeat="movie in vm.movies">
&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>
### Input variables
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="local" linenums="false">
</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.
The following are some of the key AngularJS built-in directives and their equivalents in Angular.
<table width="100%">
<col width="50%">
</col>
<col width="50%">
</col>
<tr>
<th>
AngularJS
</th>
<th>
Angular
</th>
@ -195,10 +222,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-app
<code-example>
&lt;body ng-app="movieHunter">
</code-example>
@ -211,15 +241,18 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### Bootstrapping
<code-example path="cb-ajs-quick-reference/src/main.ts" linenums="false">
</code-example>
<br>
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example>
@ -236,10 +269,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-class
<code-example format="">
&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>
### ngClass
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false">
</code-example>
@ -283,10 +321,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-click
<code-example format="">
&lt;button ng-click="vm.toggleImage()">
&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>
### Bind to the `click` event
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false">
</code-example>
@ -332,10 +375,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-controller
<code-example format="">
&lt;div ng-controller="MovieListCtrl as vm">
</code-example>
@ -346,9 +392,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### Component decorator
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example>
@ -364,8 +412,10 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-hide
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>
### Bind to the `hidden` property
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 style=top>
<td>
### ng-href
<code-example format="">
&lt;a ng-href="angularDocsUrl">Angular Docs&lt;/a>
</code-example>
@ -396,6 +450,7 @@ The following are some of the key AngularJS built-in directives and their equiva
fetches from that URL.
In AngularJS, the `ng-href` is often used to activate a route as part of navigation.
<code-example format="">
&lt;a ng-href="#movies">Movies&lt;/a>
</code-example>
@ -404,9 +459,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### Bind to the `href` property
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="href" linenums="false">
</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.
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false">
</code-example>
@ -432,10 +490,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-if
<code-example format="">
&lt;table ng-if="movies.length">
</code-example>
@ -447,9 +508,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### *ngIf
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false">
</code-example>
@ -467,10 +530,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-model
<code-example format="">
&lt;input ng-model="vm.favoriteHero"/>
</code-example>
@ -480,9 +546,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### ngModel
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false">
</code-example>
@ -499,10 +567,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-repeat
<code-example format="">
&lt;tr ng-repeat="movie in vm.movies">
</code-example>
@ -514,9 +585,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### *ngFor
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false">
</code-example>
@ -538,10 +611,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-show
<code-example format="">
&lt;h3 ng-show="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>
### Bind to the `hidden` property
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false">
</code-example>
@ -578,10 +656,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-src
<code-example format="">
&lt;img ng-src="{{movie.imageurl}}">
</code-example>
@ -592,9 +673,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### Bind to the `src` property
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="src" linenums="false">
</code-example>
@ -610,10 +693,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-style
<code-example format="">
&lt;div ng-style="{color: colorPreference}">
</code-example>
@ -627,9 +713,11 @@ The following are some of the key AngularJS built-in directives and their equiva
</td>
<td>
### ngStyle
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false">
</code-example>
@ -651,10 +739,13 @@ The following are some of the key AngularJS built-in directives and their equiva
</tr>
<tr style=top>
<td>
### ng-switch
<code-example format="">
&lt;div ng-switch="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>
### ngSwitch
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false">
</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.
For more information on pipes, see [Pipes](guide/pipes).
<table width="100%">
<col width="50%">
</col>
<col width="50%">
</col>
<tr>
<th>
AngularJS
</th>
<th>
Angular
</th>
@ -745,10 +844,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### currency
<code-example>
&lt;td>{{movie.price | currency}}&lt;/td>
</code-example>
@ -757,9 +859,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### currency
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false">
</code-example>
@ -771,10 +875,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### date
<code-example>
&lt;td>{{movie.releaseDate | date}}&lt;/td>
</code-example>
@ -783,9 +890,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### date
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="date" linenums="false">
</code-example>
@ -798,10 +907,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### filter
<code-example>
&lt;tr ng-repeat="movie in movieList | filter: {title:listFilter}">
</code-example>
@ -810,6 +922,7 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### 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.
@ -820,10 +933,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### json
<code-example>
&lt;pre>{{movie | json}}&lt;/pre>
</code-example>
@ -832,9 +948,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### json
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="json" linenums="false">
</code-example>
@ -846,10 +964,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### limitTo
<code-example>
&lt;tr ng-repeat="movie in movieList | limitTo:2:0">
</code-example>
@ -859,9 +980,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### slice
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false">
</code-example>
@ -876,10 +999,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### lowercase
<code-example>
&lt;div>{{movie.title | lowercase}}&lt;/div>
</code-example>
@ -888,9 +1014,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### lowercase
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false">
</code-example>
@ -902,10 +1030,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### number
<code-example>
&lt;td>{{movie.starRating | number}}&lt;/td>
</code-example>
@ -914,9 +1045,11 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### number
<code-example path="cb-ajs-quick-reference/src/app/app.component.html" region="number" linenums="false">
</code-example>
@ -933,10 +1066,13 @@ For more information on pipes, see [Pipes](guide/pipes).
</tr>
<tr style=top>
<td>
### orderBy
<code-example>
&lt;tr ng-repeat="movie in movieList | orderBy : 'title'">
</code-example>
@ -946,6 +1082,7 @@ For more information on pipes, see [Pipes](guide/pipes).
</td>
<td>
### none
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.
The Angular code is shown using TypeScript.
<table width="100%">
<col width="50%">
</col>
<col width="50%">
</col>
<tr>
<th>
AngularJS
</th>
<th>
Angular
</th>
@ -1000,10 +1143,13 @@ The Angular code is shown using TypeScript.
</tr>
<tr style=top>
<td>
### IIFE
<code-example>
(function () {
...
@ -1016,6 +1162,7 @@ The Angular code is shown using TypeScript.
</td>
<td>
### none
This is a nonissue in Angular because ES 2015 modules
@ -1029,10 +1176,13 @@ The Angular code is shown using TypeScript.
</tr>
<tr style=top>
<td>
### Angular modules
<code-example>
angular.module("movieHunter", ["ngRoute"]);
</code-example>
@ -1042,9 +1192,11 @@ The Angular code is shown using TypeScript.
</td>
<td>
### Angular modules
<code-example path="cb-ajs-quick-reference/src/app/app.module.1.ts" linenums="false">
</code-example>
@ -1060,10 +1212,13 @@ The Angular code is shown using TypeScript.
</tr>
<tr style=top>
<td>
### Controller registration
<code-example>
angular
.module("movieHunter")
@ -1080,9 +1235,11 @@ The Angular code is shown using TypeScript.
</td>
<td>
### Component decorator
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false">
</code-example>
@ -1101,10 +1258,13 @@ The Angular code is shown using TypeScript.
</tr>
<tr style=top>
<td>
### Controller function
<code-example>
function MovieListCtrl(movieService) {
}
@ -1114,9 +1274,11 @@ The Angular code is shown using TypeScript.
</td>
<td>
### Component class
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false">
</code-example>
@ -1133,10 +1295,13 @@ The Angular code is shown using TypeScript.
</tr>
<tr style=top>
<td>
### Dependency injection
<code-example>
MovieListCtrl.$inject = ['MovieService'];
function MovieListCtrl(movieService) {
@ -1151,9 +1316,11 @@ The Angular code is shown using TypeScript.
</td>
<td>
### Dependency injection
<code-example path="cb-ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false">
</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.
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.
<table width="100%">
<col width="50%">
</col>
<col width="50%">
</col>
<tr>
<th>
AngularJS
</th>
<th>
Angular
</th>
@ -1211,10 +1384,13 @@ also encapsulate a style sheet within a specific component.
</tr>
<tr style=top>
<td>
### Link tag
<code-example>
&lt;link href="styles.css" rel="stylesheet" />
</code-example>
@ -1224,9 +1400,11 @@ also encapsulate a style sheet within a specific component.
</td>
<td>
### Link tag
<code-example path="cb-ajs-quick-reference/src/index.html" region="style" linenums="false">
</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
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>

View File

@ -54,6 +54,7 @@ The examples in this page are available as a <live-example></live-example>.
{@a example-transitioning-between-states}
## Quickstart example: Transitioning between two states
<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>
</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:
<code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false">
</code-example>
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false">
</code-example>
@ -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.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false">
</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.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false">
</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:
<code-example path="animations/src/app/hero-list-basic.component.ts">
</code-example>
@ -128,6 +134,7 @@ component's template.
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>
@ -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:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false">
</code-example>
<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>
</figure>
@ -153,6 +162,7 @@ If several transitions have the same timing configuration, you can combine
them into the same `transition` definition:
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false">
</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 `<=>`:
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false">
</code-example>
@ -172,6 +183,7 @@ When the transition finishes, none of these styles are kept because they're not
defined in a `state`.
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false">
</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 `* => *` transition applies when *any* change between two states takes place.
<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>
</figure>
@ -198,6 +211,7 @@ leave animations.
For example the `* => void` transition applies when the element leaves the view,
regardless of what state it was in before it left.
<figure class='image-display'>
<img src="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>
@ -205,6 +219,7 @@ regardless of what state it was in before it left.
The wildcard state `*` also matches `void`.
## Example: Entering and leaving
<figure>
<img src="assets/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"> </img>
</figure>
@ -218,6 +233,7 @@ entering and leaving of elements:
For example, in the `animations` !{_array} below there are two transitions that use
the `void => *` and `* => void` syntax to animate the element in and out of the view.
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false">
</code-example>
@ -231,6 +247,7 @@ and leaves to the right.
~~~ {.l-sub-section}
These two common animations have their own aliases:
<code-example language="typescript">
transition(':enter', [ ... ]); // void => *
transition(':leave', [ ... ]); // * => void
@ -242,6 +259,7 @@ These two common animations have their own aliases:
~~~
## Example: Entering and leaving from different states
<figure>
<img src="assets/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"> </img>
</figure>
@ -258,12 +276,14 @@ is:
This gives you fine-grained control over each transition:
<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>
</figure>
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false">
</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'`
## Automatic property calculation
<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>
</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:
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false">
</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'`
* Run for 200ms, with easing: `'0.2s ease-in-out'`
<figure>
<img src="assets/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"> </img>
</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>
## Multi-step animations with keyframes
<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>
</figure>
@ -374,6 +399,7 @@ This example adds some "bounce" to the enter and leave animations with
keyframes:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false">
</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
offsets receive offsets `0`, `0.5`, and `1`.
## Parallel animation groups
<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>
</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:
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false">
</code-example>
@ -415,6 +443,7 @@ In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
those callbacks like this:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false">
</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:
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane>
@ -129,6 +132,7 @@ A few minor changes to the lone `app.component` lead to these two class and HTML
</code-tabs>
Install a few new npm dependencies with the following command:
<code-example language="none" class="code-shell">
npm install @angular/compiler-cli @angular/platform-server --save
</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.
<code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false">
</code-example>
@ -167,6 +172,7 @@ While JIT app URLs are more flexible, stick with _component-relative_ URLs for c
***Compiling the application***
Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing:
<code-example language="none" class="code-shell">
node_modules/.bin/ngc -p tsconfig-aot.json
</code-example>
@ -176,6 +182,7 @@ Initiate AOT compilation from the command line using the previously installed `n
~~~ {.l-sub-section}
Windows users should surround the `ngc` command in double quotes:
<code-example format='.'>
"node_modules/.bin/ngc" -p tsconfig-aot.json
</code-example>
@ -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:
<code-tabs>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane>
<code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts">
</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:
<code-example language="none" class="code-shell">
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
</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.
<code-example path="cb-aot-compiler/rollup-config.js" linenums="false">
</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`:
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false">
</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}:
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false">
</code-example>
@ -360,6 +374,7 @@ the code into an even smaller package going over the wire.
{@a run-rollup}
### Run Rollup
Execute the Rollup process with this command:
<code-example language="none" class="code-shell">
node_modules/.bin/rollup -c rollup-config.js
</code-example>
@ -369,6 +384,7 @@ Execute the Rollup process with this command:
~~~ {.l-sub-section}
Windows users should surround the `rollup` command in double quotes:
<code-example language="none" class="code-shell">
"node_modules/.bin/rollup" -c rollup-config.js
</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:
<code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false">
</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.
Use the same `lite-server` employed elsewhere in the documentation:
<code-example language="none" class="code-shell">
npm run lite
</code-example>
@ -413,33 +431,40 @@ The server starts, launches a browser, and the app should appear.
Here's the pertinent source code:
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane>
<code-pane title="src/index.html" path="cb-aot-compiler/src/index.html">
</code-pane>
<code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json">
</code-pane>
<code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js">
</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.
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">
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`
* Restore the SystemJS scripts like this:
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false">
</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`.
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap).
Open a _different_ terminal window and enter `npm start`.
<code-example language="none" class="code-shell">
npm start
</code-example>
@ -526,13 +554,16 @@ The JIT and AOT apps require their own `index.html` files because they setup and
Here they are for comparison:
<code-tabs>
<code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html">
</code-pane>
<code-pane title="src/index.html (JIT)" path="toh-6/src/index.html">
</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:
<code-tabs>
<code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts">
</code-pane>
<code-pane title="main.ts (JIT)" path="toh-6/src/main.ts">
</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:
<code-tabs>
<code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json">
</code-pane>
<code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json">
</code-pane>
@ -595,6 +632,7 @@ You'll need separate TypeScript configuration files such as these:
~~~ {.callout.is-helpful}
<header>
@Types and node modules
</header>
@ -617,6 +655,7 @@ Edit your `tsconfig-aot.json` to fit your project's file structure.
Rollup does the tree shaking as before.
<code-example path="toh-6/rollup-config.js" linenums="false">
</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.
<code-tabs>
<code-pane title="src/index.html" path="toh-6/src/index.html">
</code-pane>
<code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js">
</code-pane>
<code-pane title="rollup-config.js" path="toh-6/rollup-config.js">
</code-pane>
<code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json">
</code-pane>
@ -668,6 +712,7 @@ Compiling with AOT presupposes certain supporting files, most of them discussed
</code-tabs>
Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script:
<code-example language="none" class="code-shell">
node copy-dist-files
</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`:
<code-example language="none" class="code-shell">
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.
Install it:
<code-example language="none" class="code-shell">
npm install source-map-explorer --save-dev
</code-example>
Run the following command to generate the map.
<code-example language="none" class="code-shell">
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_.
<a href="assets/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image">
<figure class='image-display'>
<img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img>
</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.
<code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example>
@ -119,6 +120,7 @@ Which brings us to the _bootstrapping_ process itself.
{@a main}
<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
in the `src` folder named `src/main.ts`
<code-example path="setup/src/main.ts" linenums="false">
</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`
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>
@ -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.
<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.
You'll learn the details in the pages that follow. For now, focus on the big picture.
<figure>
<img src="assets/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"> </img>
</figure>
@ -43,6 +44,7 @@ Learn these building blocks, and you're on your way.
~~~ {.l-sub-section}
<p>
The code referenced on this page is available as a <live-example></live-example>.
</p>
@ -53,6 +55,7 @@ Learn these building blocks, and you're on your way.
## Modules
<figure>
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
</figure>
@ -98,6 +101,7 @@ that hosts all other app views. Only the _root module_ should set this `bootstra
Here's a simple root module:
<code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false">
</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.
<code-example path="architecture/src/main.ts" linenums="false">
</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.
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false">
</code-example>
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false">
</code-example>
@ -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.
### Angular libraries
<figure>
<img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
</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:
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false">
</code-example>
You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false">
</code-example>
In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false">
</code-example>
@ -195,6 +206,7 @@ Learn more from the [Angular modules](guide/ngmodule) page.
~~~
<div class='l-hr'>
</div>
@ -202,6 +214,7 @@ Learn more from the [Angular modules](guide/ngmodule) page.
## Components
<figure>
<img src="assets/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
</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.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class">
</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.
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>
## Templates
<figure>
<img src="assets/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
</figure>
@ -247,6 +263,7 @@ A template looks like regular HTML, except for a few differences. Here is a
template for our `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.html">
</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`.
The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
<figure>
<img src="assets/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"> </img>
</figure>
Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
<br class="l-clear-both">
<div class='l-hr'>
</div>
## Metadata
<figure>
<img src="assets/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"> </img>
</figure>
@ -289,6 +309,7 @@ In !{_Lang}, you attach metadata by using !{_a} **!{_decorator}**.
Here's some metadata for `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata">
</code-example>
@ -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`
so it can get the list of heroes to display.
<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>
</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
so that Angular knows what to do.
<div class='l-hr'>
</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
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
read as any experienced jQuery programmer can attest.
<figure>
<img src="assets/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"> </img>
</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:
<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>
@ -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:
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example>
@ -371,23 +397,27 @@ as with event binding.
Angular processes *all* data bindings once per JavaScript event cycle,
from the root of the application component tree through all child components.
<figure>
<img src="assets/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
</figure>
Data binding plays an important role in communication
between a template and its component.<br class="l-clear-both">
<figure>
<img src="assets/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
</figure>
Data binding is also important for communication between parent and child components.<br class="l-clear-both">
<div class='l-hr'>
</div>
## Directives
<figure>
<img src="assets/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"> </img>
</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:
<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>
@ -434,6 +465,7 @@ an existing element (typically an `<input>`)
by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example>
@ -447,12 +479,14 @@ Of course, you can also write your own directives. Components such as
`HeroListComponent` are one kind of custom directive.
<!-- PENDING: link to where to learn more about other kinds! -->
<div class='l-hr'>
</div>
## Services
<figure>
<img src="assets/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"> </img>
</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:
<code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class">
</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.
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class">
</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
application logic into services and make those services available to components through *dependency injection*.
<div class='l-hr'>
</div>
## Dependency injection
<figure>
<img src="assets/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"> </img>
</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`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor">
</code-example>
@ -535,6 +574,7 @@ Angular can call the component's constructor with those services as arguments.
This is *dependency injection*.
The process of `HeroService` injection looks a bit like this:
<figure>
<img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"> </img>
</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.
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers">
</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:
<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>
@ -579,6 +621,7 @@ Points to remember about dependency injection:
* Register *providers* with injectors.
<div class='l-hr'>
</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:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied">
</code-example>
@ -63,6 +64,7 @@ named <span ngio-ex>attribute-directives</span>.
Create the following source file in the indicated folder:
<code-example path="attribute-directives/src/app/highlight.directive.1.ts">
</code-example>
@ -120,6 +122,7 @@ Put the template in its own <span ngio-ex>app.component.html</span>
file that looks like this:
<code-example path="attribute-directives/src/app/app.component.1.html">
</code-example>
@ -127,6 +130,7 @@ file that looks like this:
Now reference this template in the `AppComponent`:
<code-example path="attribute-directives/src/app/app.component.ts">
</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.
<code-example path="attribute-directives/src/app/app.module.ts">
</code-example>
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight"> </img>
</figure>
@ -156,6 +162,7 @@ Did you remember to add the directive to the `declarations` attribute of `@NgMod
It is easy to forget!
Open the console in the browser tools and look for an error like this:
<code-example format="nocode">
EXCEPTION: Template parse errors:
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.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports">
</code-example>
@ -194,6 +202,7 @@ Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` !{_decorator}.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods">
</code-example>
@ -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.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor">
</code-example>
@ -225,6 +235,7 @@ which you declare and initialize in the constructor.
Here's the updated directive in full:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts">
</code-example>
@ -232,6 +243,7 @@ Here's the updated directive in full:
Run the app and confirm that the background color appears when
the mouse hovers over the `p` and disappears as it moves out.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"> </img>
</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:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color">
</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:
<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>
@ -269,6 +283,7 @@ Try it by adding the following directive binding variations 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>
@ -276,6 +291,7 @@ Add a `color` property to the `AppComponent`.
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>
@ -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.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</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.
<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>
@ -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`.
<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>
@ -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:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</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:
<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>
@ -335,6 +356,7 @@ If someone neglects to bind to `highlightColor`, highlight in red:
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>
@ -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:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (v2)" region="v2">
</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.
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example>
Here are the harness and directive in action.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"> </img>
</figure>
@ -377,6 +402,7 @@ Let the template developer set the default color.
Add a second **input** property to `HighlightDirective` called `defaultColor`:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (defaultColor)" region="defaultColor">
</code-example>
@ -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.
<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>
@ -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.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (defaultColor)" region="defaultColor">
</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.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"> </img>
</figure>
@ -422,33 +451,40 @@ This page covered how to:
The final source code follows:
<code-tabs>
<code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts">
</code-pane>
<code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html">
</code-pane>
<code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts">
</code-pane>
<code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts">
</code-pane>
<code-pane title="main.ts" path="attribute-directives/src/main.ts">
</code-pane>
<code-pane title="index.html" path="attribute-directives/src/index.html">
</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:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color">
</code-example>
@ -471,6 +508,7 @@ the `HighlightDirective`. You've seen it applied without 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>
@ -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:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example>

View File

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

View File

@ -55,6 +55,7 @@ In the following example, we import and register several services
in the `@Component` metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false">
</code-example>
@ -74,12 +75,14 @@ Learn more about providers [below](guide/cb-dependency-injection#providers).
Now that we've registered these services,
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example>
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false">
</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`.
<code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false">
</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`.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false">
</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.
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false">
</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:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User"> </img>
</figure>
@ -147,6 +154,7 @@ Once all the dependencies are in place, the `AppComponent` displays the user inf
### *@Injectable()*
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>
@ -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*.
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>
@ -247,6 +256,7 @@ We call this *sandboxing* because each service and component instance has its ow
<a id="hero-bios-component"></a>
Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple">
</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.
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>
@ -264,6 +275,7 @@ They'd be competing with each other to determine which hero to cache.
Each `HeroBioComponent` gets its *own* `HeroCacheService` instance
by listing the `HeroCacheService` in its metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component">
</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>
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img>
</figure>
@ -309,12 +322,14 @@ We look at this second, more interesting case in our next example.
### Demonstration
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component).
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts">
</code-example>
Focus on the template:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false">
</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,
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>
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact"> </img>
</figure>
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component">
</code-example>
Focus on the constructor parameters
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false">
</code-example>
@ -363,6 +382,7 @@ We'll come back to the `elementRef` property shortly.
~~~
Here's the `HeroBiosAndContactsComponent` in action.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img>
</figure>
@ -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
until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates
with the gratuitous "!!!", indicating that the logger was found.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"> </img>
</figure>
@ -387,6 +408,7 @@ require DOM access.
To illustrate, we've written a simplified version of the `HighlightDirective` from
the [Attribute Directives](guide/attribute-directives) chapter.
<code-example path="cb-dependency-injection/src/app/highlight.directive.ts">
</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,
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>
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios"> </img>
</figure>
@ -424,6 +448,7 @@ Angular passes this token to the injector and assigns the result to the paramete
Here's a typical example:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</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,
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>
@ -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.
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>
@ -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.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px"> </img>
</figure>
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month">
</code-example>
@ -506,6 +535,7 @@ The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class;
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>
@ -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.
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>
@ -540,6 +571,7 @@ or fake the behavior of the real class in a test case.
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>
@ -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:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false">
</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,
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>
@ -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):
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example>
The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"> </img>
</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`
which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`.
The following image, which displays the logging date, confirms the point:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px"> </img>
</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
as seen in this example.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory">
</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.
<code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false">
</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
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>
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>
@ -687,6 +728,7 @@ Instead, we use it like an interface.
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>
@ -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 `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>
@ -745,12 +788,14 @@ The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false">
</code-example>
We created the `TITLE` token like this:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false">
</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`
to display a *sorted* list of heroes.
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/sorted-heroes.png" alt="Sorted Heroes"> </img>
</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.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base">
</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.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes">
</code-example>
@ -850,6 +898,7 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false">
</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*
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>
@ -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)
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>
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>
@ -926,6 +978,7 @@ and add that provider to the `providers` array of the `@Component` metadata for
{@a alex-providers}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</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:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false">
</code-example>
Here's *Alex* and family in action:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/alex.png" alt="Alex in action"> </img>
</figure>
@ -958,6 +1013,7 @@ That means he must both *inject* the `Parent` *class-interface* to get *Alice* a
Here's *Barry*:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false">
</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:
<code-tabs>
<code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor">
</code-pane>
<code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor">
</code-pane>
@ -994,6 +1053,7 @@ which *is* what parent means.
Here's *Alice*, *Barry* and family in action:
<figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/alice.png" alt="Alice in action"> </img>
</figure>
@ -1006,6 +1066,7 @@ We [learned earlier](guide/cb-dependency-injection#class-interface) that a *clas
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>
@ -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:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false">
</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)
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>
@ -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,
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>
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>
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>
@ -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*.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false">
</code-example>
And here's how we could use it with a different parent type:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false">
</code-example>
@ -1101,6 +1169,7 @@ appear *above* the class definition.
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>

View File

@ -23,6 +23,7 @@ in which two or more components share information.
typically adorned with [@Input decorations](guide/template-syntax).
<code-example path="cb-component-communication/src/app/hero-child.component.ts">
</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.
<code-example path="cb-component-communication/src/app/hero-parent.component.ts">
</code-example>
The running application displays three heroes:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child"> </img>
</figure>
@ -49,6 +52,7 @@ The running application displays three heroes:
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>
@ -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.
<code-example path="cb-component-communication/src/app/name-child.component.ts">
</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:
<code-example path="cb-component-communication/src/app/name-parent.component.ts">
</code-example>
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter"> </img>
</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:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter">
</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:
<code-example path="cb-component-communication/src/app/version-child.component.ts">
</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.
<code-example path="cb-component-communication/src/app/version-parent.component.ts">
</code-example>
Here's the output of a button-pushing sequence:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"> </img>
</figure>
@ -128,6 +139,7 @@ Test that ***both*** input properties are set initially and that button clicks t
the expected `ngOnChanges` calls and values:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges">
</code-example>
@ -144,6 +156,7 @@ The child's `EventEmitter` property is an ***output property***,
as seen in this `VoterComponent`:
<code-example path="cb-component-communication/src/app/voter.component.ts">
</code-example>
@ -154,6 +167,7 @@ The parent `VoteTakerComponent` binds an event handler called `onVoted()` that r
payload `$event` and updates a counter.
<code-example path="cb-component-communication/src/app/votetaker.component.ts">
</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,
and the method processes it:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent"> </img>
</figure>
@ -170,6 +185,7 @@ and the method processes it:
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>
@ -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
countdown status message in its own template.
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts">
</code-example>
@ -196,6 +213,7 @@ countdown status message in its own template.
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>
@ -212,6 +230,7 @@ uses interpolation to display the child's `seconds` property.
Here we see the parent and child working together.
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer"> </img>
</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:
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests">
</code-example>
@ -258,6 +278,7 @@ is solely for the purpose of demonstration.
Here is the parent, `CountdownViewChildParentComponent`:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc">
</code-example>
@ -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.
<code-example path="cb-component-communication/src/app/mission.service.ts">
</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:
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts">
</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:
<code-example path="cb-component-communication/src/app/astronaut.component.ts">
</code-example>
@ -339,6 +363,7 @@ The *History* log demonstrates that messages travel in both directions between
the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service"> </img>
</figure>
@ -349,6 +374,7 @@ Tests click buttons of both the parent `MissionControlComponent` and the `Astron
and verify that the history meets expectations:
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service">
</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:
<code-example path="component-styles/src/app/hero-app.component.ts" linenums="false">
</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).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false">
</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.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false">
</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`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false">
</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
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>
@ -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.
<code-example path="component-styles/src/app/hero-app.component.ts">
</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}:
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls">
</code-example>
@ -208,6 +215,7 @@ You can embed styles directly into the HTML template by putting them
inside `<style>` tags.
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles">
</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.
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink">
</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.
<code-example path="component-styles/src/app/hero-details.component.css" region="import">
</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:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false">
</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
attached to it:
<code-example format="">
&lt;hero-details _nghost-pmm-5>
&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
by the generated component styles, which are in the `<head>` section of the DOM:
<code-example format="">
[_nghost-pmm-5] {
display: block;
@ -331,6 +344,7 @@ These extra selectors enable the scoping rules described in this page.
## Appendix 2: Loading styles with relative URLs
It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory:
<code-example format="nocode">
quest-summary.component.ts
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 `./`:
<code-example path="component-styles/src/app/quest-summary.component.ts">
</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:
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car">
</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:
<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>
<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>
@ -130,6 +134,7 @@ parameters and properties simultaneously.
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>
@ -149,6 +154,7 @@ The _consumer_ of `Car` has the problem. The consumer must update the car creati
something like this:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
</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:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
</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:
<code-example path="dependency-injection/src/app/car/car-factory.ts">
</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.
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false">
</code-example>
@ -226,23 +235,28 @@ start with a simplified version of the `HeroesComponent`
that from the [The Tour of Heroes](tutorial/).
<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>
<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 title="src/app/heroes/hero.ts" path="dependency-injection/src/app/heroes/hero.ts">
</code-pane>
<code-pane title="src/app/heroes/mock-heroes.ts" path="dependency-injection/src/app/heroes/mock-heroes.ts">
</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.
<code-example path="dependency-injection/src/app/heroes/hero.service.1.ts">
</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.
It remains nothing more than a class until you register it with an Angular injector.
<div id='bootstrap'>
</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.
<code-example path="dependency-injection/src/main.ts" linenums="false" title="src/main.ts (bootstrap)" region="bootstrap">
</code-example>
@ -336,6 +353,7 @@ Here's the `AppModule` that registers two providers, `UserService` and an `APP_C
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>
@ -351,6 +369,7 @@ place to register it.
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>
@ -393,13 +412,16 @@ constructor, [as discussed earlier](guide/dependency-injection#ctor-injection).
It's a small change:
<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>
<code-pane title="src/app/heroes/hero-list.component (without DI)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
</code-pane>
@ -416,6 +438,7 @@ It's a small change:
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>
@ -445,6 +468,7 @@ You _could_ create such an injector
explicitly:
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false">
</code-example>
@ -481,6 +505,7 @@ For example, you can create a new `HeroListComponent` with a mock service that y
under test:
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false">
</code-example>
@ -509,13 +534,16 @@ adding a constructor that takes a `Logger` parameter.
Here is the revision compared to the original.
<code-tabs>
<code-pane title="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts">
</code-pane>
<code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-pane>
@ -553,6 +581,7 @@ in order to inject a `Logger`.
~~~ {.callout.is-helpful}
<header>
Suggestion: add @Injectable() to every service class
</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
and, therefore, do not technically require it. Here's why:
<ul style="font-size:inherit">
<li>
<b>Future proofing:</b> No need to remember <code>@Injectable()</code> when you add a dependency later.
</li>
<li>
<b>Consistency:</b> All services follow the same rules, and you don't have to wonder why is missing.
</li>
@ -593,6 +625,7 @@ identify a class as a target for instantiation by an injector.
~~~ {.callout.is-critical}
<header>
Always include the parentheses
</header>
@ -614,6 +647,7 @@ Inject a logger into `HeroService` in two steps:
The logger service is quite simple:
<code-example path="dependency-injection/src/app/logger.service.ts">
</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}`.
<code-example path="dependency-injection/src/app/providers.component.ts" linenums="false" title="src/app/providers.component.ts (excerpt)" region="providers-logger">
</code-example>
If you forget to register the logger, Angular throws an exception when it first looks for the logger:
<code-example format="nocode">
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:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</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`.
<div id='provide'>
</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:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1">
</code-example>
@ -684,6 +723,7 @@ This is actually a shorthand expression for a provider registration
using a _provider_ object literal with two properties:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3">
</code-example>
@ -696,6 +736,7 @@ The second is a !{_secondParam},
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.
<div id='class-provider'>
</div>
@ -707,6 +748,7 @@ The following code tells the injector
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>
@ -721,6 +763,7 @@ This logger gets the user from the injected `UserService`,
which is also injected at the application level.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example>
@ -728,6 +771,7 @@ which is also injected at the application level.
Configure it like `BetterLogger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</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`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</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.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</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>
@ -779,6 +826,7 @@ Then you register a provider with the `useValue` option,
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>
@ -787,6 +835,7 @@ See more `useValue` examples in the
[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and
[OpaqueToken](guide/dependency-injection#opaquetoken) sections.
<div id='factory-provider'>
</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.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false">
</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:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false">
</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:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false">
</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:
<code-tabs>
<code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts">
</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>
@ -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:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false">
</code-example>
@ -899,6 +955,7 @@ Angular knows to inject the
service associated with that `HeroService` class token:
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature">
</code-example>
@ -908,12 +965,14 @@ This is especially convenient when you consider that most dependency values are
{@a non-class-dependencies}
### Non-class dependencies
<p>
What if the dependency value isn't a class? Sometimes the thing you want to inject is a
span string, function, or object.
</p>
<p>
Applications often define configuration objects with lots of small facts
(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>
@ -942,12 +1002,14 @@ There is no `AppConfig` class.
The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you
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>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example>
@ -972,6 +1034,7 @@ to define and use an !{opaquetoken}.
The definition looks like this:
<code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false">
</code-example>
@ -979,6 +1042,7 @@ The definition looks like this:
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>
@ -987,6 +1051,7 @@ Now you can inject the configuration object into any constructor that needs it,
the help of an `@Inject` #{_decorator}:
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false">
</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`.
<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>
<div id='optional'>
</div>
@ -1022,6 +1089,7 @@ You can tell Angular that the dependency is optional by annotating the
constructor argument with `@Optional()`:
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
</code-example>
@ -1048,6 +1116,7 @@ Developers rarely work directly with an injector, but
here's an `InjectorComponent` that does.
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector">
</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.
<code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false">
</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
loads `systemjs.config.server.js`.
<code-example path="deployment/src/index.html" region="systemjs-config" linenums="false">
</code-example>
@ -129,6 +131,7 @@ you make to `systemjs.config.js`.
Notice the `paths` key:
<code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false">
</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.
<code-tabs>
<code-pane title="index.html" path="deployment/src/index.html">
</code-pane>
<code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js">
</code-pane>
<code-pane title="main.ts" path="deployment/src/main.ts">
</code-pane>
<code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts">
</code-pane>
<code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts">
</code-pane>
<code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts">
</code-pane>
<code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts">
</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
console:
<code-example format="nocode">
Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
</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`.
<code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false">
</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
`historyApiFallback` entry in the dev server options as follows:
<code-example>
historyApiFallback: {
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)
to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
<code-example format=".">
RewriteEngine On
# 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),
modified to serve `index.html`:
<code-example format=".">
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
[here](http://stackoverflow.com/a/26152011/2116927):
<code-example format="." escape="html">
<system.webServer>
<rewrite>
@ -532,6 +549,7 @@ and to
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
<code-example format=".">
"rewrites": [ {
"source": "**",

View File

@ -14,6 +14,7 @@ conditionally show a message below the list.
The final UI looks like this:
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"> </img>
</figure>
@ -48,6 +49,7 @@ changing the template and the body of the component.
When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts">
</code-example>
@ -58,6 +60,7 @@ The revised template displays the two component properties using double curly br
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>
@ -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:
<code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body">
</code-example>
@ -91,6 +95,7 @@ in the `index.html`, finds it, instantiates an instance of `AppComponent`, and r
inside the `<my-app>` tag.
Now run the app. It should display the title and hero name:
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img>
</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}.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example>
@ -122,6 +128,7 @@ Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</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":
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (li)" region="li">
</code-example>
@ -164,6 +172,7 @@ repeat items for any [iterable](guide/!{_iterableUrl}) object.
Now the heroes appear in an unordered list.
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
</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:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)">
</code-example>
@ -196,6 +206,7 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id">
</code-example>
@ -211,6 +222,7 @@ After importing the `Hero` class, the `AppComponent.heroes` property can return
of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes">
</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.
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</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:
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (message)" region="message">
</code-example>
@ -282,23 +296,28 @@ Here's the final code:
<code-tabs>
<code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
</code-pane>
<code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
</code-pane>
<code-pane title="main.ts" path="displaying-data/src/main.ts">
</code-pane>

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.
<code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" linenums="false">
</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.
<code-tabs>
<code-pane title="ad-banner.component.ts" path="cb-dynamic-component-loader/src/app/ad-banner.component.ts">
</code-pane>
<code-pane title="ad.service.ts" path="cb-dynamic-component-loader/src/app/ad.service.ts">
</code-pane>
<code-pane title="ad-item.ts" path="cb-dynamic-component-loader/src/app/ad-item.ts">
</code-pane>
<code-pane title="app.module.ts" path="cb-dynamic-component-loader/src/app/app.module.ts">
</code-pane>
<code-pane title="app.component" path="cb-dynamic-component-loader/src/app/app.component.ts">
</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.
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" linenums="false">
</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.
<code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" linenums="false">
</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:
<code-tabs>
<code-pane title="hero-job-ad.component.ts" path="cb-dynamic-component-loader/src/app/hero-job-ad.component.ts">
</code-pane>
<code-pane title="hero-profile.component.ts" path="cb-dynamic-component-loader/src/app/hero-profile.component.ts">
</code-pane>
<code-pane title="ad.component.ts" path="cb-dynamic-component-loader/src/app/ad.component.ts">
</code-pane>
@ -140,6 +153,7 @@ Two sample components and the `AdComponent` interface are shown below:
</code-tabs>
The final ad banner looks like this:
<figure class='image-display'>
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
</figure>

View File

@ -43,13 +43,16 @@ Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, s
We bootstrap our `AppModule` in main.ts.
<code-tabs>
<code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts">
</code-pane>
<code-pane title="main.ts" path="cb-dynamic-form/src/main.ts">
</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.
<code-example path="cb-dynamic-form/src/app/question-base.ts">
</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.
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false">
</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.
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false">
</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.
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false">
</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.
`DynamicFormComponent` is the entry point and the main container for the form.
<code-tabs>
<code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html">
</code-pane>
<code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts">
</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.
<code-tabs>
<code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html">
</code-pane>
<code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts">
</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.
<code-example path="cb-dynamic-form/src/app/question.service.ts">
</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.
<code-example path="cb-dynamic-form/src/app/app.component.ts">
</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.
Saving and retrieving the data is an exercise for another time.
The final form looks like this:
<figure class='image-display'>
<img src="assets/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> </img>
</figure>

View File

@ -44,6 +44,7 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a live-example}
**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>
@ -71,6 +72,7 @@ In this first template validation example,
notice the HTML that reads the control state and updates the display appropriately.
Here's an excerpt from the template HTML for a single input control bound to the hero name:
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" linenums="false">
</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.
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class">
</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:
<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>
<code-pane title="template/hero-form-template1.component.ts" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane>
@ -160,13 +166,16 @@ the template and component.
Here's the hero name again, excerpted from the revised template
(Template 2), next to the original version:
<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>
<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>
@ -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
`#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>
@ -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.
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>
@ -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.
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>
@ -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
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>
@ -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`.
<code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" linenums="false">
</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 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>
@ -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.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" linenums="false">
</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.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
<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>
<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>
@ -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:
<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>
<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>
@ -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:
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" linenums="false">
</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.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" linenums="false">
</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.
<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>
<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 title="template/hero-form-template1.component.ts (#1)" path="cb-form-validation/src/app/template/hero-form-template1.component.ts">
</code-pane>
@ -551,6 +579,7 @@ and declared in the `SharedModule`.
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>
@ -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
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>
@ -581,6 +611,7 @@ has a `forbiddenNameValidator` at the bottom.
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
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>
@ -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
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>
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>

View File

@ -50,6 +50,7 @@ otherwise wrestle with yourself.
You'll learn to build a template-driven form that looks like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form"> </img>
</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:
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"> </img>
</figure>
@ -103,6 +105,7 @@ and one optional field (`alterEgo`).
In the `!{_appDir}` directory, create the following file with the given content:
<code-example path="forms/src/app/hero.ts">
</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:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
</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:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1">
</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:
<code-example path="forms/src/app/app.module.ts">
</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:
<code-example path="forms/src/app/app.component.ts">
</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:
<code-example path="forms/src/app/hero-form.component.html" region="start">
</code-example>
@ -258,6 +266,7 @@ Bootstrap gives the form a little style.
~~~ {.callout.is-important}
<header>
Angular forms don't require a style library
</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>`:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
</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:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers">
</code-example>
@ -300,6 +311,7 @@ you display its name using the interpolation syntax.
Running the app right now would be disappointing.
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"> </img>
</figure>
@ -319,6 +331,7 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example>
@ -341,6 +354,7 @@ adding and deleting characters, you'd see them appear and disappear
from the interpolated text.
At some point it might look like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"> </img>
</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:
<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>
@ -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:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"> </img>
</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.
You can leverage those class names to change the appearance of the control.
<table>
<tr>
<th>
State
</th>
<th>
Class if true
</th>
<th>
Class if false
</th>
@ -438,18 +459,22 @@ You can leverage those class names to change the appearance of the control.
</tr>
<tr>
<td>
The control has been visited.
</td>
<td>
<code>ng-touched</code>
</td>
<td>
<code>ng-untouched</code>
</td>
@ -458,18 +483,22 @@ You can leverage those class names to change the appearance of the control.
</tr>
<tr>
<td>
The control's value has changed.
</td>
<td>
<code>ng-dirty</code>
</td>
<td>
<code>ng-pristine</code>
</td>
@ -478,18 +507,22 @@ You can leverage those class names to change the appearance of the control.
</tr>
<tr>
<td>
The control's value is valid.
</td>
<td>
<code>ng-valid</code>
</td>
<td>
<code>ng-invalid</code>
</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.
<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>
@ -518,12 +552,14 @@ Follow these steps *precisely*:
The actions and effects are as follows:
<figure class='image-display'>
<img src="assets/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition"> </img>
</figure>
You should see the following transitions and class names:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"> </img>
</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
on the left of the input box:
<figure class='image-display'>
<img src="assets/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"> </img>
</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`:
<code-example path="forms/src/forms.css">
</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:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
</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:
<figure class='image-display'>
<img src="assets/images/devguide/forms/name-required-error.png" width="400px" alt="Name required"> </img>
</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:
<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>
@ -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.
<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>
@ -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.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset">
</code-example>
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false">
</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.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset">
</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:
<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>
@ -705,6 +751,7 @@ the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button">
</code-example>
@ -747,6 +794,7 @@ Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div">
</code-example>
@ -756,6 +804,7 @@ The main form is visible from the start because the
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>
@ -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:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted">
</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:
<aio-filetree>
<aio-folder>
angular-forms
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-form.component.html
</aio-file>
<aio-file>
hero-form.component.ts
</aio-file>
@ -832,16 +891,19 @@ The final project folder structure should look like this:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
<aio-file>
index.html
</aio-file>
@ -850,11 +912,13 @@ The final project folder structure should look like this:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</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:
<code-tabs>
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
</code-pane>
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
</code-pane>
<code-pane title="hero.ts" path="forms/src/app/hero.ts">
</code-pane>
<code-pane title="app.module.ts" path="forms/src/app/app.module.ts">
</code-pane>
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
</code-pane>
<code-pane title="main.ts" path="forms/src/main.ts">
</code-pane>
<code-pane title="index.html" path="forms/src/index.html">
</code-pane>
<code-pane title="forms.css" path="forms/src/forms.css">
</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`
open simultaneously.
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"> </img>
</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:
<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>
@ -128,6 +130,7 @@ Each tax return component has the following characteristics:
* 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.
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"> </img>
</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.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts">
</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.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts">
</code-example>
@ -166,6 +171,7 @@ What a mess!
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example>
@ -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`.
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/car-components.png" alt="car components" width="220"> </img>
</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
`Tires` resolved by the root injector (A).
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"> </img>
</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
that you translate into Spanish:
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false">
</code-example>
@ -92,6 +93,7 @@ that you translate into Spanish:
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>
@ -103,6 +105,7 @@ need a description of the message.
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>
@ -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>`):
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false">
</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:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false">
</code-example>
@ -141,6 +146,7 @@ Here are two techniques to try.
(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>
@ -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:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false">
</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:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false">
</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:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false">
</code-example>
@ -240,6 +249,7 @@ property, which outputs either an "m" or an "f".
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>
@ -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.
If you haven't already installed the CLI and its `platform-server` peer dependency, do so now:
<code-example language="sh" class="code-shell">
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:
<code-example language="sh" class="code-shell">
./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
by adding the `--i18nFormat=xmb` flag.
<code-example language="sh" class="code-shell">
./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
file is located somewhere other than in the root folder,
you must identify the path to it with the `-p` option:
<code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n -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`
to make the command easier to remember and run:
<code-example format='.' language='sh'>
"scripts": {
"i18n": "ng-xi18n",
@ -324,6 +339,7 @@ to make the command easier to remember and run:
</code-example>
Now you can issue command variations such as these:
<code-example language="sh" class="code-shell">
npm run i18n
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:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</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,
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>
@ -406,6 +424,7 @@ See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
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>
@ -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:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false">
</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:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</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.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false">
</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.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false">
</code-example>
@ -460,11 +483,13 @@ The second translation unit, immediately below the first one, contains the `sele
Here they are together, after translation:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false">
</code-example>
<div class='l-main-content'>
</div>
@ -472,6 +497,7 @@ Here they are together, after translation:
The entire template translation is complete. It's
time to incorporate that translation into the application.
<div id='app-pre-translation'>
</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:
<code-tabs>
<code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html">
</code-pane>
<code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts">
</code-pane>
<code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts">
</code-pane>
<code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html">
</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:
<code-example path="cb-i18n/src/index.html" region="i18n" linenums="false">
</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.
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>
@ -584,6 +618,7 @@ The `getTranslationProviders` function in the following `src/app/i18n-providers.
creates those providers based on the user's _locale_
and the corresponding translation file:
<code-example path="cb-i18n/src/app/i18n-providers.ts">
</code-example>
@ -613,6 +648,7 @@ You'll create an _options_ object with the translation providers from `getTransl
and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows:
<code-example path="cb-i18n/src/main.ts" linenums="false">
</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
For this sample, the Spanish language command would be
<code-example language="sh" class="code-shell">
./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}
Windows users may have to quote the command:
<code-example language="sh" class="code-shell">
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf

View File

@ -15,25 +15,31 @@ The documentation is divided into major thematic sections, each
a collection of pages devoted to that theme.
<table width="100%">
<col width="15%">
</col>
<col>
</col>
<tr style=top>
<td>
<b><a href="../quickstart.html">QuickStart</a></b>
</td>
<td>
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>.
@ -43,13 +49,16 @@ a collection of pages devoted to that theme.
</tr>
<tr style=top>
<td>
<b>Guide</b>
</td>
<td>
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,
@ -60,13 +69,16 @@ a collection of pages devoted to that theme.
</tr>
<tr style=top>
<td>
<b><a href="../api/">API Reference</a></b>
</td>
<td>
Authoritative details about each of the Angular libraries.
</td>
@ -75,13 +87,16 @@ a collection of pages devoted to that theme.
</tr>
<tr style=top>
<td>
<b><a href="../tutorial/">Tutorial</a></b>
</td>
<td>
A step-by-step, immersive approach to learning Angular that
introduces the major features of Angular in an application context.
@ -91,13 +106,16 @@ a collection of pages devoted to that theme.
</tr>
<tr style=top>
<td>
<b><a href=" ">Advanced</a></b>
</td>
<td>
In-depth analysis of Angular features and development practices.
</td>
@ -106,13 +124,16 @@ a collection of pages devoted to that theme.
</tr>
<tr style=top if-docs="ts">
<td>
<b><a href="../cookbook/">Cookbook</a></b>
</td>
<td>
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
<figure>
<img src="assets/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"> </img>
</figure>

View File

@ -7,6 +7,7 @@ Angular calls lifecycle hook methods on directives and components as it creates,
@description
<figure>
<img src="assets/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"> </img>
</figure>
@ -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()`
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>
@ -48,25 +50,31 @@ Angular only calls a directive/component hook method *if it is defined*.
## Lifecycle sequence
*After* creating a component/directive by calling its constructor, Angular
calls the lifecycle hook methods in the following sequence at specific moments:
<table width="100%">
<col width="20%">
</col>
<col width="80%">
</col>
<tr>
<th>
Hook
</th>
<th>
Purpose and Timing
</th>
@ -75,13 +83,16 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr>
<tr style=top>
<td>
<code>ngOnChanges()</code>
</td>
<td>
Respond when Angular (re)sets data-bound input properties.
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 style=top>
<td>
<code>ngOnInit()</code>
</td>
<td>
Initialize the directive/component after Angular first displays the data-bound 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 style=top>
<td>
<code>ngDoCheck()</code>
</td>
<td>
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 style=top>
<td>
<code>ngAfterContentInit()</code>
</td>
<td>
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 style=top>
<td>
<code>ngAfterContentChecked()</code>
</td>
<td>
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 style=top>
<td>
<code>ngAfterViewInit()</code>
</td>
<td>
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 style=top>
<td>
<code>ngAfterViewChecked()</code>
</td>
<td>
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 style=top>
<td>
<code>ngOnDestroy</code>
</td>
<td>
Cleanup just before Angular destroys the directive/component.
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:
<table width="100%">
<col width="20%">
</col>
<col width="80%">
</col>
<tr>
<th>
Component
</th>
<th>
Description
</th>
@ -281,13 +319,16 @@ Here's a brief description of each exercise:
</tr>
<tr style=top>
<td>
<a href="#peek-a-boo">Peek-a-boo</a>
</td>
<td>
Demonstrates every lifecycle hook.
Each hook method writes to the on-screen log.
@ -297,13 +338,16 @@ Here's a brief description of each exercise:
</tr>
<tr style=top>
<td>
<a href="#spy">Spy</a>
</td>
<td>
Directives have lifecycle hooks too.
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 style=top>
<td>
<a href="#onchanges">OnChanges</a>
</td>
<td>
See how Angular calls the `ngOnChanges()` hook with a `changes` object
every time one of the component input properties changes.
@ -334,13 +381,16 @@ Here's a brief description of each exercise:
</tr>
<tr style=top>
<td>
<a href="#docheck">DoCheck</a>
</td>
<td>
Implements an `ngDoCheck()` method with custom change detection.
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 style=top>
<td>
<a href="#afterview">AfterView</a>
</td>
<td>
Shows what Angular means by a *view*.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
@ -366,13 +419,16 @@ Here's a brief description of each exercise:
</tr>
<tr style=top>
<td>
<a href="#aftercontent">AfterContent</a>
</td>
<td>
Shows how to project external content into a component and
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 style=top>
<td>
Counter
</td>
<td>
Demonstrates a combination of a component and a directive
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.
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
<figure class='image-display'>
<img src="assets/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"> </img>
</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`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false">
</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.
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>
@ -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>`
with an entry in the *Hook Log* as seen here:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"> </img>
</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).
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>
@ -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`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false">
</code-example>
@ -579,12 +644,14 @@ The example component, `OnChangesComponent`, has two input properties: `hero` an
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>
Here's the sample in action as the user makes changes.
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img>
</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:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false">
</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`
so you can see how often `DoCheck` is called. The results are illuminating:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"> </img>
</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>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false">
</code-example>
The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false">
</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).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false">
</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.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false">
</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
for one turn of the browser's JavaScript cycle and that's just long enough.
Here's *AfterView* in action:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"> </img>
</figure>
@ -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
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>
@ -724,6 +799,7 @@ into the component*.
Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false">
</code-example>
@ -731,6 +807,7 @@ Now look at the component's template:
The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
In this case, the projected content is the `<my-child>` from the parent.
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"> </img>
</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).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false">
</code-example>

View File

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

View File

@ -63,6 +63,7 @@ General
* [What is the Angular compiler?](guide/ngmodule-faq#q-angular-compiler)
* [Can you summarize the NgModule API?](guide/ngmodule-faq#q-ngmodule-api)
<div class='l-hr'>
</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 them in _this_ module if they _belong_ to this module.
<div class='l-hr'>
</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.
They're the _only_ classes that you can add to `declarations`.
<div class='l-hr'>
</div>
@ -113,6 +116,7 @@ For example, don't declare FORMS_DIRECTIVES from `@angular/forms`.
* Non-Angular classes and objects, such as
strings, numbers, functions, entity models, configurations, business logic, and helper classes.
<div class='l-hr'>
</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
as well as dynamically loaded in a pop-up dialog.
<div class='l-hr'>
</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?
The "x" class isn't visible to other modules until you add it to the `exports` list.
<div class='l-hr'>
</div>
@ -178,6 +184,7 @@ components, directives, and pipes.
Import only [BrowserModule](guide/ngmodule-faq#q-browser-vs-common-module) in the root `AppModule`.
<div class='l-hr'>
</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.
<div class='l-hr'>
</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'.
<div class='l-hr'>
</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.
A module can even export a module that it doesn't import.
<div class='l-hr'>
</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.
It's only purpose is to add http service providers to the application as a whole.
<div class='l-hr'>
</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.
Angular's own `BrowserModule` exports a couple of modules like this:
<code-example>
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>
@ -344,6 +357,7 @@ configure services in root and feature modules respectively.
Angular doesn't recognize these names but Angular developers do.
Follow this convention when you write similar modules with configurable service providers.
<div class='l-hr'>
</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,
not just the classes declared in the `HeroModule`.
<div class='l-hr'>
</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,
Angular prefers service instances created from these providers to the service instances of the application root injector.
<div class='l-hr'>
</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 `AppModule` always wins.
<div class='l-hr'>
</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>`.
Define child routes and let the router load module components into that outlet.
<div class='l-hr'>
</div>
@ -524,6 +542,7 @@ In the NgModule page sample applications, if you had registered `UserService` in
the `HeroComponent` couldn't inject it.
The application would fail the moment a user navigated to "Heroes".
<div class='l-hr'>
</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),
not the root `AppComponent`.
<div class='l-hr'>
</div>
@ -598,6 +618,7 @@ The username goes bonkers as the Angular creates a new `UserService` instance ea
~~~
<div class='l-hr'>
</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.
So Angular creates a new child injector for the lazy-loaded module context.
<div class='l-hr'>
</div>
@ -650,11 +672,13 @@ You can throw an error or take other remedial action.
Certain NgModules (such as `BrowserModule`) implement such a guard,
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>
<div class='l-hr'>
</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.
<div class='l-hr'>
</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,
although doing so is harmless.
<div class='l-hr'>
</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)
in the templates of other components.
<div class='l-hr'>
</div>
@ -778,6 +805,7 @@ If a component isn't an _entry component_ or wasn't found in a template,
the compiler omits it.
<div class='l-hr'>
</div>
@ -854,15 +882,19 @@ follow them unless you have a good reason to do otherwise.
~~~
<table>
<tr>
<th style="vertical-align: top">
Feature Module
</th>
<th style="vertical-align: top">
Guidelines
</th>
@ -871,13 +903,16 @@ follow them unless you have a good reason to do otherwise.
</tr>
<tr>
<td style="vertical-align: top">
<a id="domain-feature-module"></a>Domain
</td>
<td>
Domain feature modules deliver a user experience *dedicated to a particular application domain*
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>
<td style="vertical-align: top">
<a id="routed-feature-module"></a>Routed
</td>
<td>
_Routed feature modules_ are _domain feature modules_
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>
<td style="vertical-align: top">
<a id="routing-module"></a>Routing
</td>
<td>
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>
<td style="vertical-align: top">
<a id="service-feature-module"></a>Service
</td>
<td>
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>
<td style="vertical-align: top">
<a id="widget-feature-module"></a>Widget
</td>
<td>
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>
<tr>
<th>
Feature Module
</th>
<th>
Declarations
</th>
<th>
Providers
</th>
<th>
Exports
</th>
<th>
Imported By
</th>
<th>
Examples
</th>
@ -1101,33 +1156,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr>
<tr>
<td>
Domain
</td>
<td>
Yes
</td>
<td>
Rare
</td>
<td>
Top component
</td>
<td>
Feature, <code>AppModule</code>
</td>
<td>
<code>ContactModule</code> (before routing)
</td>
@ -1136,33 +1198,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr>
<tr>
<td>
Routed
</td>
<td>
Yes
</td>
<td>
Rare
</td>
<td>
No
</td>
<td>
Nobody
</td>
<td>
<code>ContactModule</code>, <code>HeroModule</code>, <code>CrisisModule</code>
</td>
@ -1171,33 +1240,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr>
<tr>
<td>
Routing
</td>
<td>
No
</td>
<td>
Yes (Guards)
</td>
<td>
<code>RouterModule</code>
</td>
<td>
Feature (for routing)
</td>
<td>
<code>AppRoutingModule</code>, <code>ContactRoutingModule</code>, <code>HeroRoutingModule</code>
</td>
@ -1206,33 +1282,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr>
<tr>
<td>
Service
</td>
<td>
No
</td>
<td>
Yes
</td>
<td>
No
</td>
<td>
<code>AppModule</code>
</td>
<td>
<code>HttpModule</code>, <code>CoreModule</code>
</td>
@ -1241,33 +1324,40 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</tr>
<tr>
<td>
Widget
</td>
<td>
Yes
</td>
<td>
Rare
</td>
<td>
Yes
</td>
<td>
Feature
</td>
<td>
<code>CommonModule</code>, <code>SharedModule</code>
</td>
@ -1279,6 +1369,7 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
</table>
<div class='l-hr'>
</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).
Within each file you write an `export` statement to make parts of the module public:
<code-example format='.'>
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:
<code-example format='.'>
import { AppComponent } from './app.component';
@ -1342,17 +1435,20 @@ They are available _everywhere_.
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>
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>
<div class='l-hr'>
</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
or exported by a module that this module imports.
<div class='l-hr'>
</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
how to link this module with other modules.
<div class='l-hr'>
</div>
@ -1416,15 +1514,19 @@ how to link this module with other modules.
## NgModule API
The following table summarizes the `NgModule` metadata properties.
<table>
<tr>
<th>
Property
</th>
<th>
Description
</th>
@ -1433,13 +1535,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr>
<tr>
<td style="vertical-align: top">
<code>declarations</code>
</td>
<td>
A list of [declarable](guide/ngmodule-faq#q-declarable) classes,
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>
<td style="vertical-align: top">
<code>providers</code>
</td>
<td>
A list of dependency-injection providers.
@ -1492,13 +1600,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr>
<tr>
<td style="vertical-align: top">
<code>imports</code>
</td>
<td>
A list of supporting modules.
@ -1521,13 +1632,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr>
<tr>
<td style="vertical-align: top">
<code>exports</code>
</td>
<td>
A list of declarations&mdash;*component*, *directive*, and *pipe* classes&mdash;that
an importing module can use.
@ -1539,7 +1653,7 @@ The following table summarizes the `NgModule` metadata properties.
Declarations are private by default.
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' must import `CommonModule` itself.
@ -1556,13 +1670,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr>
<tr>
<td style="vertical-align: top">
<code>bootstrap</code>
</td>
<td>
A list of components that can be bootstrapped.
@ -1579,13 +1696,16 @@ The following table summarizes the `NgModule` metadata properties.
</tr>
<tr>
<td style="vertical-align: top">
<code>entryComponents</code>
</td>
<td>
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.
Read this page before reading those FAQs.
<div class='l-hr'>
</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:
<code-example path="setup/src/app/app.module.ts" linenums="false">
</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:
<code-example path="ngmodule/src/app/app.component.0.ts" linenums="false">
</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.
<code-example path="ngmodule/src/main.ts" linenums="false">
</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.
<code-example path="ngmodule/src/main-static.ts" linenums="false">
</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.
This is the last time you'll look at `main.ts`.
<div class='l-hr'>
</div>
@ -218,12 +224,14 @@ As the app evolves,
the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives)
that sets the background color of the attached element.
<code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false">
</code-example>
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>
@ -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:
<code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false">
</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`.
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>
<code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false">
</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,
using an input binding to set the `subtitle`.
<code-example path="ngmodule/src/app/app.component.1.ts" linenums="false">
</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`.
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>
@ -287,6 +300,7 @@ accessible through a user service.
This sample application has a dummy implementation of such a `UserService`.
<code-example path="ngmodule/src/app/user.service.ts" linenums="false">
</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.
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>
@ -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`
and sets the component's `user` property from the service.
<code-example path="ngmodule/src/app/title.component.ts" linenums="false">
</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,
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>
@ -321,6 +338,7 @@ add it to a `providers` property in the `AppModule` metadata:
In the revised `TitleComponent`, an `*ngIf` directive guards the message.
There is no message if there is no user.
<code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false">
</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.
The initial version of `AppModule` imports `BrowserModule`.
<code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false">
</code-example>
@ -384,6 +403,7 @@ import the `ReactiveFormsModule`.
The `ContactComponent` selector matches an element named `<app-contact>`.
Add an element with that name to the `AppComponent` template, just below the `<app-title>`:
<code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false">
</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
and break the component into three constituent HTML, TypeScript, and css files:
<code-tabs>
<code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html">
</code-pane>
<code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts">
</code-pane>
<code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css">
</code-pane>
<code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts">
</code-pane>
<code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts">
</code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</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.
<code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false">
</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.
Update the `declarations` in the `AppModule` accordingly:
<code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false">
</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.
<code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false">
</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:
<code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false">
</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.
The app file structure looks like this:
<aio-filetree>
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
highlight.directive.ts
</aio-file>
<aio-file>
title.component.(html|ts)
</aio-file>
<aio-file>
user.service.ts
</aio-file>
<aio-folder>
contact
<aio-file>
awesome.pipe.ts
</aio-file>
<aio-file>
contact.component.(css|html|ts)
</aio-file>
<aio-file>
contact.service.ts
</aio-file>
<aio-file>
highlight.directive.ts
</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.
<code-tabs>
<code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts">
</code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane>
@ -736,6 +782,7 @@ It's easy to refactor the contact material into a contact feature module.
Here's the new `ContactModule`:
<code-example path="ngmodule/src/app/contact/contact.module.2.ts">
</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.
<code-tabs>
<code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts">
</code-pane>
<code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts">
</code-pane>
@ -835,6 +885,7 @@ Some facets of the current application merit discussion are as follows:
The new `AppComponent` template has
a title, three links, and a `<router-outlet>`.
<code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false">
</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:
<code-example path="ngmodule/src/app/app.module.3.ts">
</code-example>
@ -870,6 +922,7 @@ that handles the app's routing concerns.
### App routing
<code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false">
</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:
<code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false">
</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
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>
@ -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,
and the app is ready to navigate.
<code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false">
</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`.
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>
@ -967,13 +1024,16 @@ that has both shared [declarables](cookbook/ngmodule-faq) and services.
`ContactModule` has changed in two small but important ways.
<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>
<code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class">
</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
a more interesting and useful example. Its file structure is as follows:
<aio-filetree>
<aio-folder>
hero
<aio-file>
hero-detail.component.ts
</aio-file>
<aio-file>
hero-list.component.ts
</aio-file>
<aio-file>
hero.component.ts
</aio-file>
<aio-file>
hero.module.ts
</aio-file>
<aio-file>
hero-routing.module.ts
</aio-file>
<aio-file>
hero.service.ts
</aio-file>
<aio-file>
highlight.directive.ts
</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.
<code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false">
</code-example>
@ -1087,6 +1157,7 @@ and share them with the modules that need them.
Here is the `SharedModule`:
<code-example path="ngmodule/src/app/shared/shared.module.ts">
</code-example>
@ -1166,6 +1237,7 @@ Perform the following steps:
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>
@ -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:
<code-tabs>
<code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4">
</code-pane>
<code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts">
</code-pane>
@ -1250,13 +1325,16 @@ Here is the updated `AppModule` paired with version 3 for comparison:
### A trimmer _ContactModule_
Here is the new `ContactModule` paired with the prior version:
<code-tabs>
<code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts">
</code-pane>
<code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts">
</code-pane>
@ -1269,6 +1347,7 @@ Notice the following:
* The imports include `SharedModule` instead of `CommonModule` and `FormsModule`.
* The new version is leaner and cleaner.
<div class='l-hr'>
</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`.
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>
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>
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>
@ -1334,6 +1416,7 @@ Remember to _import_ the result; don't add it to any other `@NgModule` list.
~~~
<div class='l-hr'>
</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.
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>

View File

@ -53,6 +53,7 @@ The `package.json` includes two sets of packages,
The *dependencies* are essential to *running* the application.
The *devDependencies* are only necessary to *develop* the application.
You can exclude them from production installations by adding `--production` to the install command, as follows:
<code-example format="." language="bash">
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.
<code-example path="pipes/src/app/hero-birthday1.component.ts" linenums="false">
</code-example>
@ -38,6 +39,7 @@ a human-friendly date.
Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false">
</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*.
Safari and other older browsers don't support it. You can add support with a polyfill.
<code-example language="html">
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt;
@ -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>**:
<code-example path="pipes/src/app/app.component.html" region="format-birthday" linenums="false">
</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:
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" linenums="false">
</code-example>
@ -112,6 +117,7 @@ That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`).
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" linenums="false">
</code-example>
@ -120,6 +126,7 @@ As you click the button, the displayed date alternates between
"**<samp>04/15/1988</samp>**" and
"**<samp>Friday, April 15, 1988</samp>**".
<figure class='image-display'>
<img src='assets/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"> </img>
</figure>
@ -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>**.
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" linenums="false">
</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.
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" linenums="false">
</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:
<code-example path="pipes/src/app/exponential-strength.pipe.ts" linenums="false">
</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.
<code-example path="pipes/src/app/power-booster.component.ts" linenums="false">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/pipes/power-booster.png' alt="Power Booster"> </img>
</figure>
@ -210,6 +222,7 @@ Note the following:
~~~ {.callout.is-helpful}
<header>
Remember the
</header>
@ -232,11 +245,13 @@ Upgrade the example to a "Power Boost Calculator" that combines
your pipe and two-way data binding with `ngModel`.
<code-example path="pipes/src/app/power-boost-calculator.component.ts">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"> </img>
</figure>
@ -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:
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" linenums="false">
</code-example>
The companion component class provides heroes, adds heroes into the #{_array}, and can reset the #{_array}.
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" linenums="false">
</code-example>
@ -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.
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" linenums="false">
</code-example>
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" linenums="false">
</code-example>
@ -295,6 +314,7 @@ It's just using a different change-detection algorithm that ignores changes to t
Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false">
</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
code with checkbox switches and additional displays to help you experience these effects.
<figure class='image-display'>
<img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img>
</figure>
@ -338,6 +359,7 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false">
</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`.
The complete implementation is as follows:
<code-tabs>
<code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure">
</code-pane>
<code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure">
</code-pane>
@ -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.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" title="src/app/flying-heroes.pipe.ts (filter)" region="filter">
</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`.
<code-example path="pipes/src/app/flying-heroes" linenums="false" title="src/app/flying-heroes (_region)" region="_region">
</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_
display updates as you add heroes, even when you mutate the `heroes` #{_array}.
<h3 id='async-pipe'>
The impure <i> AsyncPipe </i>
</h3>
@ -436,6 +464,7 @@ This next example binds an `#{_Observable}` of message strings
(`message#{_dollar}`) to a view with the `async` pipe.
<code-example path="pipes/src/app/hero-async-message.component.ts">
</code-example>
@ -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>:
<code-example path="pipes/src/app/fetch-json.pipe.ts">
</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.
<code-example path="pipes/src/app/hero-list.component.ts">
</code-example>
The component renders as the following:
<figure class='image-display'>
<img src='assets/images/devguide/pipes/hero-list.png' alt="Hero List"> </img>
</figure>
@ -489,6 +521,7 @@ It displays the same hero data in JSON format by chaining through to the built-i
~~~ {.callout.is-helpful}
<header>
Debugging with the json pipe
</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 list might be sorted by hero `name` and `planet` of origin properties in the following way:
<code-example language="html">
&lt;!-- NOT REAL CODE! -->
&lt;div *ngFor="let hero of heroes | orderBy:'name,planet'">&lt;/div>

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.
<code-example path="reactive-forms/src/app/data-model.ts" linenums="false">
</code-example>
@ -175,6 +176,7 @@ Make a new file called
`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>
@ -182,6 +184,7 @@ Make a new file called
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>
@ -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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false">
</code-example>
@ -259,6 +264,7 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
1. Add `HeroDetailComponent` to the declarations array.
<code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false">
</code-example>
@ -270,6 +276,7 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
## Display 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>
@ -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`:
<code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false">
</code-example>
Now that everything is wired up, the browser should display something like this:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/just-formcontrol.png" width="400px" alt="Single FormControl"> </img>
</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`:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false">
</code-example>
@ -331,6 +341,7 @@ of `hero-detail.component.ts`:
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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false">
</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`:
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example>
@ -397,6 +410,7 @@ closing `form` tag in the `hero-detail.component.html`:
The `heroForm.value` returns the _form model_.
Piping it through the `JsonPipe` renders the model as JSON in the browser:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/json-output.png" width="400px" alt="JSON output"> </img>
</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`:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false">
</code-example>
@ -435,6 +450,7 @@ by following this plan:
The revised `HeroDetailComponent` looks like this:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false">
</code-example>
@ -454,6 +470,7 @@ demonstrates the simplicity of using `Validators.required` in reactive forms.
First, import the `Validators` symbol.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false">
</code-example>
@ -464,6 +481,7 @@ The first item is the initial value for `name`;
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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example>
The browser displays the following:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/validators-json-output.png" width="400px" alt="Single FormControl"> </img>
</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 `<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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false">
</code-example>
@ -521,6 +543,7 @@ Then add corresponding markup in `hero-detail.component.html`
within the `form` element.
<code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false">
</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;
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>
@ -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:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false">
</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_
with the nested address `FormGroup`:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/address-group.png" width="400px" alt="JSON output"> </img>
</figure>
@ -614,6 +640,7 @@ page by adding the following to the template,
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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false">
</code-example>
You can use this technique to display _any_ property of a `FormControl`
such as one of the following:
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="10%">
</col>
<col width="90%">
</col>
<tr>
<th>
Property
</th>
<th>
Description
</th>
@ -659,13 +694,16 @@ such as one of the following:
</tr>
<tr>
<td>
<code>myControl.value</code>
</td>
<td>
the value of a `FormControl`.
</td>
@ -674,13 +712,16 @@ such as one of the following:
</tr>
<tr>
<td>
<code>myControl.status</code>
</td>
<td>
the validity of a `FormControl`. Possible values: `VALID`,
`INVALID`, `PENDING`, or `DISABLED`.
@ -690,13 +731,16 @@ such as one of the following:
</tr>
<tr>
<td>
<code>myControl.pristine</code>
</td>
<td>
`true` if the user has _not_ changed the value in the UI.
Its opposite is `myControl.dirty`.
@ -706,13 +750,16 @@ such as one of the following:
</tr>
<tr>
<td>
<code>myControl.untouched</code>
</td>
<td>
`true` if the control user has not yet entered the HTML control
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`:
<code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false">
</code-example>
@ -771,6 +819,7 @@ Recall the definition of `Hero` in `data-model.ts`:
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>
@ -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.
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>
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>
@ -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`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false">
</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.
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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false">
</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.
<code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false">
</code-example>
@ -872,12 +927,14 @@ as the following steps demonstrate.
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>
Add the `hero` input property.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false">
</code-example>
@ -885,6 +942,7 @@ Add the `hero` input property.
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>
@ -896,6 +954,7 @@ control values from the previous hero are cleared and
status flags are restored to the _pristine_ state.
You could call `reset` at the top of `ngOnChanges` like this.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false">
</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`.
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>
@ -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.
Together they look a bit like this:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/hero-list.png" width="420px" alt="HeroListComponent"> </img>
</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`:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false">
</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,
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>
@ -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_!
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>
@ -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`,
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>
@ -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`.
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>
@ -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:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false">
</code-example>
Here's the complete template for the _secret lairs_ section:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array">
</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.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false">
</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.
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false">
</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".
"Magneta" doesn't have an address, as you can see in the diagnostic JSON at the bottom of the form.
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array"> </img>
</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`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false">
</code-example>
Call it in the constructor, after creating the form.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false">
</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.
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>
@ -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.
After you implement both features in this section, the form will look like this:
<figure class='image-display'>
<img src="assets/images/devguide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons"> </img>
</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_
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>
@ -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.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false">
</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_.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false">
</code-example>
@ -1217,6 +1295,7 @@ Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _fo
### Buttons
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>
@ -1249,43 +1328,52 @@ This page covered:
The key files of the final version are as follows:
<code-tabs>
<code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts">
</code-pane>
<code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts">
</code-pane>
<code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html">
</code-pane>
<code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html">
</code-pane>
<code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts">
</code-pane>
<code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts">
</code-pane>
<code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts">
</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.
<h2 id='report-issues'>
Reporting vulnerabilities
</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/).
<h2 id='best-practices'>
Best practices
</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.
<h2 id='xss'>
Preventing cross-site scripting (XSS)
</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:
<code-example path="security/src/app/inner-html-binding.component.html">
</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:
<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>
@ -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>`
tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element.
<figure class='image-display'>
<img src='assets/images/devguide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'> </img>
</figure>
@ -154,6 +160,7 @@ carries a high risk of introducing template-injection vulnerabilities.
<h2 id='bypass-security-apis'>
Trusting safe values
</h2>
@ -179,6 +186,7 @@ your intended use of the value. Imagine that the following template needs to bin
`javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL">
</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:
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'> </img>
</figure>
@ -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>`:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example>
@ -218,6 +230,7 @@ Angular to allow binding into `<iframe src>`:
<h2 id='http'>
HTTP-level vulnerabilities
</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
on the server side, but Angular provides helpers to make integration on the client side easier.
<h3 id='xsrf'>
Cross-site request forgery
</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.
An Angular application can customize cookie and header names by providing its own `CookieXSRFStrategy` values.
<code-example language="typescript">
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name') }
</code-example>
Or you can implement and provide an entirely custom `XSRFStrategy`:
<code-example language="typescript">
{ provide: XSRFStrategy, useClass: MyXSRFStrategy }
@ -291,6 +307,7 @@ The Stanford University paper
See also Dave Smith's easy-to-understand
<a href="https://www.youtube.com/watch?v=9inczw6qtpY" target="_blank" title="Cross Site Request Funkery Securing Your Angular Apps From Evil Doers">talk on XSRF at AngularConnect 2016</a>.
<h3 id='xssi'>
Cross-site script inclusion (XSSI)
</h3>
@ -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).
<h2 id='code-review'>
Auditing Angular applications
</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).
The root `AppComponent` orchestrates these demos:
<code-example path="server-communication/src/app/app.component.ts">
</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`.
<code-example path="server-communication/src/app/app.module.1.ts" linenums="false">
</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)**.
It works like this:
<figure class='image-display'>
<img src='assets/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250"> </img>
</figure>
This demo has a single component, the `HeroListComponent`. Here's its template:
<code-example path="server-communication/src/app/toh/hero-list.component.html">
</code-example>
@ -154,6 +158,7 @@ Below the button is an area for an error message.
### The *HeroListComponent* class
Here's the component class:
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component">
</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
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>
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>
@ -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
[injected](guide/dependency-injection) into the `HeroService` constructor.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor">
</code-example>
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>
@ -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.
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>
@ -244,6 +254,7 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U
## Process the response object
Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false">
</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.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false">
</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}
<h3>
<b> HeroListComponent </b> error handling
</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.
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false">
</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:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false">
</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.
The body of the request should look like this:
<code-example format="." language="javascript">
{ "name": "Windstorm" }
</code-example>
@ -389,6 +405,7 @@ with its own `data` property.
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>
@ -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.
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>
<h2 id='cors'>
Cross-Origin Requests: Wikipedia example
</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
types in a text box:
<figure class='image-display'>
<img src='assets/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250"> </img>
</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`.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts">
</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:
<code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false">
</code-example>
In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper:
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false">
</code-example>
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false">
</code-example>
@ -512,6 +536,7 @@ to the server.
Now that you have a service that can query the Wikipedia API,
turn your attention to the component (template and class) that takes user input and displays search results.
<code-example path="server-communication/src/app/wiki/wiki.component.ts">
</code-example>
@ -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.
It should only make requests when the user *stops typing*.
Here's how it will work after refactoring:
<figure class='image-display'>
<img src='assets/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"> </img>
</figure>
@ -580,13 +606,16 @@ with the help of some nifty Observable operators.
Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`:
<code-tabs>
<code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts">
</code-pane>
<code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts">
</code-pane>
@ -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_
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>
@ -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.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false">
</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
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>
@ -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`.
<code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false">
</code-example>
Then it registers the provider in the root `AppModule`.
<code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false">
</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,
<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>
@ -737,6 +772,7 @@ posed by top-level JSON arrays.
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>
@ -767,12 +803,14 @@ are !{_array}s of objects in those collections.
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>
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>
@ -782,6 +820,7 @@ Finally, redirect client HTTP requests to the in-memory web API by
adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
At the same time, call its `forRoot()` configuration method with the `HeroData` class.
<code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false">
</code-example>
@ -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.
<code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)">
</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.
This cookbook explains how to do it.**See the <live-example name="cb-set-document-title"></live-example>**.
<table>
<tr>
<td>
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,
@ -22,6 +25,7 @@ This cookbook explains how to do it.**See the <live-example name="cb-set-documen
</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>
</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 obvious approach is to bind a property of the component to the HTML `<title>` like this:
<code-example format=''>
&lt;title&gt;{{This_Does_Not_Work}}&lt;/title&gt;
</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:
<code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false">
</code-example>
We bind that method to three anchor tags and, voilà!
<figure class='image-display'>
<img src="assets/images/cookbooks/set-document-title/set-title-anim.gif" alt="Set title"> </img>
</figure>
@ -79,18 +86,22 @@ We bind that method to three anchor tags and, voilà!
Here's the complete solution
<code-tabs>
<code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts">
</code-pane>
<code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts">
</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.
If you do, this page can help you understand their purpose.
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="10%">
</col>
<col width="90%">
</col>
<tr>
<th>
File
</th>
<th>
Purpose
</th>
@ -47,13 +54,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>src/app/</code>
</td>
<td>
Angular application files go here.
@ -70,13 +80,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>e2e/</code>
</td>
<td>
_End-to-end_ (e2e) tests of the application,
written in Jasmine and run by the
@ -90,13 +103,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>node_modules/</code>
</td>
<td>
The _npm_ packages installed with the `npm install` command.
</td>
@ -105,8 +121,10 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code> .editorconfig<br>
.git/<br>
@ -115,6 +133,7 @@ If you do, this page can help you understand their purpose.
</td>
<td>
Tooling configuration files and folders.
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>
<td>
<code>CHANGELOG.md</code>
</td>
<td>
The history of changes to the _QuickStart_ repository.
Delete or ignore.
@ -140,13 +162,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>favicon.ico</code>
</td>
<td>
The application icon that appears in the browser tab.
</td>
@ -155,13 +180,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>index.html</code>
</td>
<td>
The application host page.
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>
<td>
<code>karma.conf.js</code>
</td>
<td>
Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
test runner described in the [Testing](guide/testing) guide.
@ -191,13 +222,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>karma-test-shim.js</code>
</td>
<td>
Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
with SystemJS as described in the [Testing](guide/testing) guide.
@ -207,13 +241,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>non-essential-files.txt</code>
</td>
<td>
A list of files that you can delete if you want to purge your setup of the
original QuickStart Seed testing and git maintainence artifacts.
@ -226,13 +263,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>LICENSE</code>
</td>
<td>
The open source MIT license to use this setup code in your application.
</td>
@ -241,13 +281,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>package.json</code>
</td>
<td>
Identifies `npm `package dependencies for the project.
@ -261,13 +304,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>protractor.config.js</code>
</td>
<td>
Configuration for the
<a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a>
@ -278,13 +324,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>README.md</code>
</td>
<td>
Instruction for using this git repository in your project.
Worth reading before deleting.
@ -294,13 +343,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>styles.css</code>
</td>
<td>
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>
<td>
<code>systemjs<br>.config.js</code>
</td>
<td>
Tells the **SystemJS** module loader where to find modules
referenced in JavaScript `import` statements. For example:
<code-example language="ts">
import { Component } from '@angular/core;
</code-example>
@ -331,13 +387,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>systemjs<br>.config.extras.js</code>
</td>
<td>
Optional extra SystemJS configuration.
A way to add SystemJS mappings, such as for appliation _barrels_,
@ -348,13 +407,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>tsconfig.json</code>
</td>
<td>
Tells the TypeScript compiler how to transpile TypeScript source files
into JavaScript files that run in all modern browsers.
@ -364,13 +426,16 @@ If you do, this page can help you understand their purpose.
</tr>
<tr>
<td>
<code>tslint.json</code>
</td>
<td>
The `npm` installed TypeScript linter inspects your TypeScript code
and complains when you violate one of its rules.

View File

@ -32,6 +32,7 @@ Then ...
Perform the _clone-to-launch_ steps with these terminal commands.
<code-example language="sh" class="code-shell">
git clone .git 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>
and unzip it into your project folder. Then perform the remaining steps with these terminal commands.
<code-example language="sh" class="code-shell">
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:
### OS/X (bash)
<code-example language="sh" class="code-shell">
xargs rm -rf &lt; non-essential-files.osx.txt
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>
### Windows
<code-example language="sh" class="code-shell">
for /f %i in (non-essential-files.txt) do del %i /F /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}
Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<aio-filetree>
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
@ -142,6 +151,7 @@ Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
</aio-folder>
<aio-file>
main.ts
</aio-file>
@ -154,18 +164,22 @@ Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<code-tabs>
<code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts">
</code-pane>
<code-pane title="src/main.ts" path="setup/src/main.ts">
</code-pane>
@ -185,30 +199,37 @@ unless told to do otherwise.
The following are all in `src/`
<style>
td, th {vertical-align: top}
</style>
<table width="100%">
<col width="20%">
</col>
<col width="80%">
</col>
<tr>
<th>
File
</th>
<th>
Purpose
</th>
@ -217,13 +238,16 @@ The following are all in `src/`
</tr>
<tr>
<td>
<ngio-ex>app/app.component.ts</ngio-ex>
</td>
<td>
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
@ -234,13 +258,16 @@ The following are all in `src/`
</tr>
<tr if-docs="ts">
<td>
<code>app/app.module.ts</code>
</td>
<td>
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`.
@ -251,13 +278,16 @@ The following are all in `src/`
</tr>
<tr>
<td>
<ngio-ex>main.ts</ngio-ex>
</td>
<td>
Compiles the application with the [JIT compiler](glossary) and
[bootstraps](guide/appmodule)

View File

@ -7,6 +7,7 @@ Angular has a powerful template engine that lets us easily manipulate the DOM st
@description
<style>
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -49,6 +50,7 @@ Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</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:
<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>
@ -79,6 +82,7 @@ and how to [write your own](guide/structural-directives#unless) structural direc
~~~ {.callout.is-helpful}
<header>
Directive spelling
</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.
<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>
@ -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.
Confirm that fact using browser developer tools to inspect the DOM.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM"> </img>
</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`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (display-none)" region="display-none">
</code-example>
While invisible, the element remains in the DOM.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM"> </img>
</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.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (asterisk)" region="asterisk">
</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.
<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>
@ -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.
<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>
@ -222,6 +233,7 @@ Then it translates the template _attribute_ into a template _element_, wrapped a
None of these forms are actually rendered.
Only the finished product ends up in the DOM.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"> </img>
</figure>
@ -242,6 +254,7 @@ template _attribute_ to template _element_.
Here's a full-featured application of `NgFor`, written all three ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example>
@ -351,6 +364,7 @@ The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`,
Here's an example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example>
@ -381,6 +395,7 @@ As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr">
</code-example>
@ -388,6 +403,7 @@ can be desugared into the template _attribute_ 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>
@ -420,12 +436,14 @@ those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (template-tag)" region="template-tag">
</code-example>
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"> </img>
</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.
<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>
@ -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.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example>
@ -466,6 +486,7 @@ neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-span)" region="ngif-span">
</code-example>
@ -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.
<code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span">
</code-example>
The constructed paragraph renders strangely.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"> </img>
</figure>
@ -492,12 +515,14 @@ You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this,
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-span)" region="select-span">
</code-example>
the drop down is empty.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work"> </img>
</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>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example>
It renders properly.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"> </img>
</figure>
@ -525,12 +552,14 @@ It renders properly.
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example>
The drop down works properly.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"> </img>
</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 more like the curly braces in a JavaScript `if`-block:
<code-example language="javascript">
if (someCondition) {
statement1;
@ -563,6 +593,7 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1">
</code-example>
@ -580,6 +611,7 @@ Creating a directive is similar to creating a component.
Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton">
</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.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor">
</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>
@ -648,6 +682,7 @@ Nobody reads the `myUnless` property so it doesn't need a getter.
The completed directive code looks like this:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example>
@ -657,6 +692,7 @@ Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleV
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>
@ -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 truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
<figure class='image-display'>
<img src='assets/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"> </img>
</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.
<code-tabs>
<code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts">
</code-pane>
<code-pane title="app.component.html" path="structural-directives/src/app/app.component.html">
</code-pane>
<code-pane title="app.component.css" path="structural-directives/src/app/app.component.css">
</code-pane>
<code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts">
</code-pane>
<code-pane title="hero.ts" path="structural-directives/src/app/hero.ts">
</code-pane>
<code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts">
</code-pane>
<code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts">
</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:
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import">
</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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport">
</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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport">
</code-pane>
@ -235,23 +250,28 @@ _ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto">
</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:
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata">
</code-pane>
@ -303,29 +328,35 @@ See these variations side-by-side:
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>
The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property:
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl">
</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:
<code-tabs>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js">
</code-pane>
@ -373,6 +407,7 @@ next to the original _ES5_ version for comparison:
~~~ {.callout.is-helpful}
<header>
Name the constructor
</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
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>
@ -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
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>
@ -410,6 +447,7 @@ adding the _defined property_ in raw JavaScript like this:
There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`:
<code-example>
app.MyDirective = ng.core.Directive({
selector: '[myDirective]'
@ -419,6 +457,7 @@ You can define a directive with `ng.core.Directive`:
</code-example>
and a pipe with `ng.core.Pipe`:
<code-example>
app.MyPipe = ng.core.Pipe({
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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js">
</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>
@ -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_.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts">
</code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6">
</code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js">
</code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl">
</code-pane>
@ -567,28 +618,34 @@ last parameter is the class constructor itself.
This format should be familiar to AngularJS developers.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js">
</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>
@ -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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js">
</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>
@ -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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js">
</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>
@ -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.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6">
</code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js">
</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>
@ -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_.
These particular _TypeScript_ and _ES6_ code snippets happen to be identical.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts">
</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>
@ -815,23 +893,28 @@ The `queries` property value is a hash map.
* each _value_ is a new instance of either `ViewChild` or `ViewChildren`.
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view">
</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>
@ -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).
<code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content">
</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>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content">
</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>

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.
Most developers feel that *this particular error* is more annoying than helpful.
You can suppress them with the following additional flag:
<code-example format=".">
"suppressImplicitAnyIndexErrors":true
@ -92,6 +93,7 @@ like `Promise` if the target is `es6`.
Since the QuickStart is targeting `es5`, you can override the
list of declaration files to be included:
<code-example format=".">
"lib": ["es2015", "dom"]

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:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false">
</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:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false">
</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.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false">
</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
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>
@ -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.
Here's what the UI displays:
<code-example>
a | ab | abc | ab | a | |
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/user-input/keyup1-anim.gif' alt="key up 1"> </img>
</figure>
@ -91,6 +97,7 @@ Here's what the UI displays:
Alternatively, you could accumulate the individual keys themselves by substituting `event.key`
for `event.target.value` in which case the same user input would produce:
<code-example>
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:
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false">
</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
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>
@ -152,6 +161,7 @@ and the component does nothing.
Type something in the input box, and watch the display update with each keystroke.
<figure class='image-display'>
<img src='assets/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back"> </img>
</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
`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>
@ -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.
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>
Here's how it works.
<figure class='image-display'>
<img src='assets/images/devguide/user-input/keyup3-anim.gif' alt="key up 3"> </img>
</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.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false">
</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
clicking **Add**.
<figure class='image-display'>
<img src='assets/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"> </img>
</figure>
@ -231,6 +246,7 @@ clicking **Add**.
Below is the "Little Tour of Heroes" component.
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false">
</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.
<code-tabs>
<code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts">
</code-pane>
<code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts">
</code-pane>
<code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts">
</code-pane>
<code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts">
</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
<h2 id='prereq1'>
Prerequisite: Node.js
</h2>
@ -64,6 +65,7 @@ Older versions produce errors.
<h2 id='prereq2'>
Prerequisite: Visual Studio 2015 Update 3
</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.
<h2 id='prereq3'>
Prerequisite: Configure External Web tools
</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.
<h2 id='prereq4'>
Prerequisite: Install TypeScript 2 for Visual Studio 2015
</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.
<h2 id='download'>
Step 1: Download the QuickStart files
</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.
<h2 id='create-project'>
Step 2: Create the Visual Studio ASP.NET project
</h2>
@ -141,6 +147,7 @@ no authentication and no hosting. Pick the template and options appropriate for
<h2 id='copy'>
Step 3: Copy the QuickStart files into the ASP.NET project folder
</h2>
@ -158,6 +165,7 @@ Include the files in the Visual Studio project as follows:
* tsconfig.json
<h2 id='restore'>
Step 4: Restore the required packages
</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.
<h2 id='build-and-run'>
Step 5: Build and run the app
</h2>
@ -195,6 +204,7 @@ Try editing any of the project files. *Save* and refresh the browser to
see the changes.
<h2 id='routing'>
Note on Routing Applications
</h2>

View File

@ -6,6 +6,7 @@ Create Angular applications with a Webpack based tooling.
@description
<style>
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -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`:
<code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false">
</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.
<code-example path="webpack/src/app/app.component.ts" region="component" linenums="false">
</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:
<div class='code-example'>
<code-example name="webpack.config.js (single output)" language="javascript">
output: {
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`:
<div class='code-example'>
<code-example language="javascript">
entry: {
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,
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>
@ -156,8 +164,10 @@ Webpack _itself_ only understands JavaScript files.
Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
Configure loaders for TypeScript and CSS as follows.
<div class='code-example'>
<code-example language="javascript">
rules: [
{
@ -178,8 +188,10 @@ Configure loaders for TypeScript and CSS as follows.
When Webpack encounters `import` statements like the following,
it applies the `test` RegEx patterns.
<div class='code-example'>
<code-example language="typescript">
import { AppComponent } from './app.component.ts';
@ -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.
Tap into that pipeline with plugins such as the `uglify` minification plugin:
<div class='code-example'>
<code-example language="javascript">
plugins: [
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.
Create a new project folder.
<code-example language="sh" class="code-shell">
mkdir angular-webpack
cd angular-webpack
@ -240,28 +255,34 @@ Create a new project folder.
Add these files:
<code-tabs>
<code-pane title="package.json" path="webpack/package.webpack.json">
</code-pane>
<code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json">
</code-pane>
<code-pane title="webpack.config.js" path="webpack/webpack.config.js">
</code-pane>
<code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js">
</code-pane>
<code-pane title="config/helpers.js" path="webpack/config/helpers.js">
</code-pane>
@ -284,6 +305,7 @@ They are listed in the updated `packages.json`.
~~~
Open a terminal window and install the npm packages.
<code-example language="sh" class="code-shell">
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.
<code-example path="webpack/src/polyfills.ts" linenums="false">
</code-example>
@ -311,6 +334,7 @@ Add a `polyfills.ts` like this one to the `src/` folder.
~~~ {.callout.is-critical}
<header>
Loading polyfills
</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`.
<code-example path="webpack/config/webpack.common.js" linenums="false">
</code-example>
@ -359,6 +384,7 @@ and exports several objects as properties of a `module.exports` object.
The first export is the `entry` object:
<code-example path="webpack/config/webpack.common.js" region="entries" linenums="false">
</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.
You could write `import` statements with explicit extensions like this example:
<div class='code-example'>
<code-example language="typescript">
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).
<code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false">
</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:
<code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false">
</code-example>
@ -451,6 +481,7 @@ Multiple loaders can be chained using the array notation.
Finally, create instances of three plugins:
<code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false">
</code-example>
@ -503,6 +534,7 @@ These files tend to be short and simple.
Here is the `webpack.dev.js` development configuration file.
<code-example path="webpack/config/webpack.dev.js" linenums="false">
</code-example>
@ -525,6 +557,7 @@ other configuration options in this file.
Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell">
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.
<code-example path="webpack/config/webpack.prod.js" linenums="false">
</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:
<code-example path="webpack/src/main.ts" region="enable-prod" linenums="false">
</code-example>
Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell">
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.
<code-example path="webpack/config/webpack.test.js" linenums="false">
</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:
<code-example path="webpack/config/karma.conf.js" linenums="false">
</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.
<code-example path="webpack/config/karma-test-shim.js" linenums="false">
</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.
Grab the app code at the end of this guide and try:
<code-example language="sh" class="code-shell">
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.
<code-tabs>
<code-pane title="src/index.html" path="webpack/src/index.html">
</code-pane>
<code-pane title="src/main.ts" path="webpack/src/main.ts">
</code-pane>
<code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css">
</code-pane>
@ -648,28 +692,34 @@ Webpack techniques covered in this guide.
<code-tabs>
<code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html">
</code-pane>
<code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css">
</code-pane>
<code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts">
</code-pane>
@ -687,13 +737,16 @@ on the image and download it to that folder.
{@a bundle-ts}
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
<code-tabs>
<code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts">
</code-pane>
<code-pane title="src/vendor.ts" path="webpack/src/vendor.ts">
</code-pane>

View File

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

View File

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

View File

@ -1,38 +1,46 @@
@description
<h2>
Victor Savkin's Blog Posts
</h2>
<ul>
<li>
<a href="http://victorsavkin.com/post/137821436516/managing-state-in-angular-2-applications"> Managing State in Angular 2 Applications </a>
</li>
<li>
<a href="http://victorsavkin.com/post/114168430846/two-phases-of-angular-2-applications">Two Phases of Angular 2 Applications</a>
</li>
<li>
<a href="http://angularjs.blogspot.com/2015/03/forms-in-angular-2.html">Forms in Angular 2</a>
</li>
<li>
<a href="http://victorsavkin.com/post/110170125256/change-detection-in-angular-2">Change detection</a>
</li>
<li>
<a href="http://victorsavkin.com/post/108837493941/better-support-for-functional-programming-in">Functional programming </a>
</li>
<li>
<a href="http://victorsavkin.com/post/102965317996/angular-2-bits-unified-dependency-injection">Dependency injection</a>
</li>
@ -42,23 +50,28 @@
<h2>
<span class="icon-play-circle-outline"></span> Videos
</h2>
<h4>
Intro Videos
</h4>
<ul>
<li>
<a href="https://www.youtube.com/watch?v=uD6Okha_Yj0">Building a Todo App</a> by David East
</li>
<li>
<a href="https://www.youtube.com/watch?v=4C4bmDOV5hk">Angular 2 Forms</a> by David East
</li>
@ -67,33 +80,40 @@
</ul>
<h4>
ng-conf
</h4>
<ul>
<li>
Playlist <a href="https://www.youtube.com/watch?v=QHulaj5ZxbI&index=1&list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7">of ng-conf 2015 videos</a>.
</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.
</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.
</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.
</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.
</li>
@ -102,13 +122,16 @@
</ul>
<h4>
ng-europe
</h4>
<ul>
<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.
</li>
@ -118,33 +141,40 @@
<h2>
<span class="icon-content-copy"></span> API Design Docs &amp; Notes
</h2>
<ul>
<li>
<a href="https://drive.google.com/open?id=0B7GYXx6a6d8QR3lTT1J3MEpRSlE&authuser=0">Best Practices</a>
</li>
<li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbacUnUxc3l5aTZrbVk&authuser=0">API Design Docs</a>
</li>
<li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbacMEZDc2NtWS1VZ1k&authuser=0">Meeting Notes</a>
</li>
<li>
<a href="https://drive.google.com/open?id=0BxgtL8yFJbaceGc2dlhGQnMzYXc&authuser=0">Presentations</a>
</li>
<li>
<a href="http://goo.gl/sj0Nk1">More...</a>
</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"
view and the most heroic heroes:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard"> </img>
</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
where you can change the hero's name.
<figure class='image-display'>
<img src='assets/images/devguide/toh/hero-details-1.png' alt="Details of hero in app"> </img>
</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.
If you click "Heroes," the app displays the "Heroes" master list view.
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app"> </img>
</figure>
@ -64,12 +67,14 @@ editable details of the selected hero.
The following diagram captures all of the navigation options.
<figure class='image-display'>
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
</figure>
Here's the app in action:
<figure class='image-display'>
<img src='assets/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action"> </img>
</figure>

View File

@ -11,19 +11,25 @@ named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>.
The file structure should look like this:
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
@ -32,26 +38,31 @@ The file structure should look like this:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -60,11 +71,13 @@ The file structure should look like this:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</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
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
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."
<code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false">
</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.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false">
</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.
<code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false">
</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`.
<code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false">
</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.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero-2">
</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>
@ -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:
<code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false">
</code-example>
@ -207,6 +228,7 @@ of external modules that the app uses.
The updated `AppModule` looks like this:
<code-example path="toh-1/src/app/app.module.ts">
</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:
<code-example path="toh-1/src/app/app.component.ts">
</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.
If your structure doesn't match, go back to that page to figure out what you missed.
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</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-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</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-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</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
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
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.
<code-example path="toh-2/src/app/app.component.ts" region="hero-array">
</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>
@ -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>
@ -147,6 +164,7 @@ and display them individually.
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>
@ -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>
@ -193,6 +212,7 @@ To add styles to your component, set the `styles` property on the `@Component` d
to the following CSS classes:
<code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false">
</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>
@ -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>
@ -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:
<code-example path="toh-2/src/app/app.component.ts" region="selected-hero">
</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.
<code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false">
</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>
@ -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,
visible in the browser's console:
<code-example format="nocode">
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.
<code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false">
</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
like this:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-selected.png' alt="Selected hero"> </img>
</figure>
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>
@ -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:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false">
</code-example>
After clicking "Magneta", the list should look like this:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app"> </img>
</figure>
@ -371,6 +402,7 @@ After clicking "Magneta", the list should look like this:
Here's the complete `app.component.ts` as of now:
<code-example path="toh-2/src/app/app.component.ts">
</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.
If not, go back to the previous pages.
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
@ -42,26 +48,31 @@ If not, go back to the previous pages.
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -70,11 +81,13 @@ If not, go back to the previous pages.
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</aio-file>
@ -106,6 +119,7 @@ The `HeroDetailComponent` class goes in the `hero-detail.component.ts` file.
Start writing the `HeroDetailComponent` as follows:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false">
</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:
<code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false">
</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.
Add that property to the `HeroDetailComponent` class like this:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero">
</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.
<code-example path="toh-3/src/app/hero.ts" linenums="false">
</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.
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>
@ -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`.
The binding will look like this:
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</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.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false">
</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
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>
@ -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.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false">
</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`.
<code-example path="toh-3/src/app/hero-detail.component.ts">
</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.
<code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import">
</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.
<code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false">
</code-example>
@ -264,6 +289,7 @@ Coordinate the master `AppComponent` with the `HeroDetailComponent`
by binding the `selectedHero` property of the `AppComponent`
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>
@ -273,6 +299,7 @@ Now every time the `selectedHero` changes, the `HeroDetailComponent` gets a new
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>
@ -297,29 +324,37 @@ without touching the parent `AppComponent`.
### Review the app structure
Verify that you have the following structure:
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
@ -328,26 +363,31 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -356,11 +396,13 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</aio-file>
@ -374,23 +416,28 @@ Verify that you have the following structure:
Here are the code files discussed in this page.
<code-tabs>
<code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts">
</code-pane>
<code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts">
</code-pane>
<code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts">
</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.
If not, go back to the previous pages.
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
@ -53,26 +61,31 @@ If not, go back to the previous pages.
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -81,11 +94,13 @@ If not, go back to the previous pages.
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</aio-file>
@ -99,6 +114,7 @@ If not, go back to the previous pages.
## Keep the app transpiling and running
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
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.
<code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false">
</code-example>
@ -158,6 +175,7 @@ consistency and future-proofing.
Add a `getHeroes()` method stub.
<code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false">
</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.
<code-example path="toh-4/src/app/mock-heroes.ts">
</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,
add an uninitialized `heroes` property:
<code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false">
</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.
The `HeroService` looks like this:
<code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false">
</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.
<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>
@ -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:
<code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false">
</code-example>
@ -234,6 +257,7 @@ Instead of using the *new* line, you'll add two lines.
Add the constructor:
<code-example path="toh-4/src/app/app.component.1.ts" region="ctor">
</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`.
If you ran the code now, Angular would fail with this error:
<code-example format="nocode">
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
</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>
@ -273,6 +299,7 @@ The service is in a `heroService` private variable.
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>
@ -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:
<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>
@ -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):
<code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false">
</code-example>
Add the implementation for the `OnInit` interface to your export statement:
<code-example format="nocode">
export class AppComponent implements OnInit {}
</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
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>
@ -328,6 +359,7 @@ when you click on a hero name.
The `HeroService` returns a list of mock heroes immediately;
its `getHeroes()` signature is synchronous.
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
</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:
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false">
</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.
<code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false">
</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:
<code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false">
</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
Verify that you have the following structure after all of your refactoring:
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
<aio-file>
hero.service.ts
</aio-file>
<aio-file>
mock-heroes.ts
</aio-file>
@ -445,26 +490,31 @@ Verify that you have the following structure after all of your refactoring:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -473,11 +523,13 @@ Verify that you have the following structure after all of your refactoring:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</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.
<code-tabs>
<code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts">
</code-pane>
<code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts">
</code-pane>
<code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts">
</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,
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>

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:
<figure class='image-display'>
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
</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.
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
hero.service.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
<aio-file>
mock-heroes.ts
</aio-file>
@ -76,26 +87,31 @@ Before continuing with the Tour of Heroes, verify that you have the following st
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -104,11 +120,13 @@ Before continuing with the Tour of Heroes, verify that you have the following st
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</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
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
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.
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.
## Action plan
@ -165,13 +183,14 @@ move the display of *Heroes* out of `AppComponent` and into its own `HeroesCompo
### *HeroesComponent*
`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.
Do the following:
* Rename the <span ngio-ex>app.component.ts</span> file as <span ngio-ex>heroes.component.ts</span>.
* Rename the `AppComponent` class as `HeroesComponent` (rename locally, _only_ in this file).
* Rename the selector `my-app` as `my-heroes`.
* Rename the <span ngio-ex>app.component.ts</span> file to <span ngio-ex>heroes.component.ts</span>.
* Rename the `AppComponent` class to `HeroesComponent` (rename locally, _only_ in this file).
* 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">
@ -181,7 +200,7 @@ Do the following:
### Create *AppComponent*
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:
@ -201,13 +220,16 @@ The first draft looks like this:
<code-tabs>
<code-pane title="src/app/app.component.ts (v1)" path="toh-5/src/app/app.component.1.ts">
</code-pane>
<code-pane title="src/app/app.module.ts (v1)" path="toh-5/src/app/app.module.1.ts">
</code-pane>
@ -216,7 +238,7 @@ The first draft looks like this:
</code-tabs>
The app still runs and displays heroes.
## Adding routing
## Add routing
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.
@ -234,6 +256,7 @@ Open `index.html` and ensure there is a `<base href="...">` element
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>
@ -243,6 +266,7 @@ at the top of the `<head>` section.
~~~ {.callout.is-important}
<header>
base href is essential
</header>
@ -267,6 +291,7 @@ pastes a URL into the browser address bar.
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>
@ -306,6 +331,7 @@ Instead, add an anchor tag to the template that, when clicked, triggers navigati
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>
@ -345,6 +371,7 @@ and the list of heroes displays.
`AppComponent` now looks like this:
<code-example path="toh-pt5/src/app/app.component.1.ts" region="v2">
</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.
<code-example path="toh-pt5/src/app/dashboard.component.1.ts" linenums="false" title="src/app/dashboard.component.ts (v1)" region="v1">
</code-example>
@ -371,20 +399,22 @@ import the dashboard component and
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>
### 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
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 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>
@ -404,6 +434,7 @@ of the [Routing & Navigation](guide/router) page.
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>
@ -428,6 +459,7 @@ Replace the `template` metadata with a `templateUrl` property that points to a n
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>
@ -436,6 +468,7 @@ template file.
Create that file with this content:
<code-example path="toh-pt5/src/app/dashboard.component.1.html">
</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.
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (imports)" region="imports">
</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:
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (class)" region="class">
</code-example>
@ -472,9 +507,9 @@ This kind of logic is also used in the `HeroesComponent`:
* Define a `heroes` !{_array} property.
* 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.
@ -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
hero object with a binding like this:
<code-example language="html">
&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 could expect to see a URL such as this:
<code-example format="nocode">
/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*.
<code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail">
</code-example>
@ -542,6 +580,7 @@ is revised and ready to be navigated to.
Here's what the `HeroDetailComponent` looks like now:
<code-example path="toh-4/src/app/hero-detail.component.ts">
</code-example>
@ -555,6 +594,7 @@ in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with
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>
@ -563,6 +603,7 @@ Inject the `!{_ActivatedRoute}`, `HeroService`, and `Location` services
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>
@ -570,16 +611,18 @@ into the constructor, saving their values in private fields:
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>
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
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>
@ -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.
The hero `id` is a number. Route parameters are always strings.
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,
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">
@ -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.
<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>
@ -621,7 +666,7 @@ using the `Location` service you injected previously.
Going back too far could take users out of the app.
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.
<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>
@ -637,6 +683,7 @@ Migrate the template to its own file
called <span ngio-ex>hero-detail.component.html</span>:
<code-example path="toh-pt5/src/app/hero-detail.component.html">
</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>
@ -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:
<code-example path="toh-pt5/src/app/dashboard.component.html" region="click">
</code-example>
@ -681,29 +730,12 @@ token in the parameterized hero detail route definition that you added to
`!{_appRoutingTsVsAppComp}` earlier:
<code-example path="toh-pt5/_file + ' (hero detail)'" linenums="false" title="_file + ' (hero detail)' (hero-detail)" region="hero-detail">
</code-example>
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*
@ -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.
<code-example path="toh-4/src/app/app.component.ts" region="template" linenums="false">
</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:
<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>
After clicking a hero, users should see something like this below the hero list:
<figure class='image-display'>
<img src='assets/images/devguide/toh/mini-hero-detail.png' alt="Mini Hero Detail" height="70"> </img>
</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 ( | ).
<code-example path="toh-pt5/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (pipe)" region="pipe">
</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:
<code-tabs>
<code-pane title="src/app/heroes.component.html" path="toh-5/src/app/heroes.component.html">
</code-pane>
<code-pane title="src/app/heroes.component.css" path="toh-5/src/app/heroes.component.css">
</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>
@ -826,7 +866,8 @@ This approach requires the following changes to the component class:
1. Import the `router` from the Angular router library.
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">
@ -834,12 +875,13 @@ This approach requires the following changes to the component class:
</code-example>
Note that you're passing a two-element *link parameters !{_array}*&mdash;a
path and the route parameter&mdash;to
the `router.navigate()` method, just as you did in the `[routerLink]` binding
!{_pathVsName} and the route parameter&mdash;to
the router `navigate()` method, just as you did in the `[routerLink]` binding
back in the `DashboardComponent`.
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>
@ -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:
<code-example path="toh-pt5/src/app/dashboard.component.ts" linenums="false" title="src/app/dashboard.component.ts (styleUrls)" region="css">
</code-example>
@ -884,11 +927,13 @@ Here's the content for the component CSS files.
<code-tabs>
<code-pane title="src/app/hero-detail.component.css" path="toh-5/src/app/hero-detail.component.css">
</code-pane>
<code-pane title="src/app/dashboard.component.css" path="toh-5/src/app/dashboard.component.css">
</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.
<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>
@ -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:
<code-example path="toh-pt5/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (styleUrls)" region="styleUrls">
</code-example>
@ -949,6 +996,7 @@ These correspond to the full set of master styles that you installed earlier dur
Here's an excerpt:
<code-example path="toh-pt5/src/styles.css" linenums="false" title="src/styles.css (excerpt)" region="toh">
</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.
<code-example path="toh-pt5/src/index.html" linenums="false" title="src/index.html (link ref)" region="css">
</code-example>
Look at the app now. The dashboard, heroes, and navigation links are styled.
<figure class='image-display'>
<img src='assets/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations"> </img>
</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:
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.css
</aio-file>
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
app-routing.module.ts
</aio-file>
<aio-file>
dashboard.component.css
</aio-file>
<aio-file>
dashboard.component.html
</aio-file>
<aio-file>
dashboard.component.ts
</aio-file>
<aio-file>
hero.service.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.css
</aio-file>
<aio-file>
hero-detail.component.html
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
<aio-file>
heroes.component.css
</aio-file>
<aio-file>
heroes.component.html
</aio-file>
<aio-file>
heroes.component.ts
</aio-file>
<aio-file>
mock-heroes.ts
</aio-file>
@ -1066,26 +1136,31 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -1094,11 +1169,13 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</aio-file>

View File

@ -24,6 +24,7 @@ That's the starting point for this page.
## Keep the app transpiling and running
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
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.
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.
<h1>
Providing HTTP Services
</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.
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 `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`.
<code-example path="toh-pt6/src/app/app.module.ts" region="v1">
</code-example>
@ -62,20 +64,14 @@ Notice that you also supply `!{_HttpModule}` as part of the *imports* !{_array}
## 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,
the HTTP client will fetch and save data from
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:
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (v2)" region="v2">
</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.
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (in-mem-web-api)" region="in-mem-web-api">
</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:
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" linenums="false">
</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.
<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>
@ -132,6 +131,7 @@ fetching heroes with an HTTP client, which must be an asynchronous operation.
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>
@ -139,6 +139,7 @@ Now convert `getHeroes()` to use HTTP.
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>
@ -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.
<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>
@ -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:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (rxjs)" region="rxjs">
</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.
<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>
@ -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.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (catch)" region="catch">
</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.
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (handleError)" region="handleError">
</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.
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.
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>
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.
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
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>
Add the following `save` method, which persists hero name changes using the hero service
`update` method and then navigates back to the previous view.
Add the following `save()` method, which persists hero name changes using the hero service
`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>
### 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:
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (add)" region="add">
</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.
<code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (add)" region="add">
</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
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">
@ -345,7 +358,8 @@ Refresh the browser and create some heroes.
Each hero in the heroes view should have a delete button.
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">
@ -355,6 +369,7 @@ name in the repeated `<li>` tag.
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>
@ -367,6 +382,7 @@ select the hero that the user will delete.
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>
@ -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
from the !{_array} and resets the selected hero, if necessary.
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>
### 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:
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (delete)" region="delete">
</code-example>
Refresh the browser and try the new delete functionality.
<div id='observables'>
</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.
You may start one 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
easy with *!{Observable}s*.
A *request-cancel-new-request* sequence is difficult to implement with *!{_Promise}s*, but
easy with *!{_Observable}s*.
### Add the ability to search by name
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.
<code-example path="toh-pt6/src/app/hero-search.service.ts">
</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()`,
after chaining it to another RxJS operator, <code>map()</code>,
to extract heroes from the response data.
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
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.
<code-example path="toh-pt6/src/app/hero-search.component.html">
</code-example>
Also, add styles for the new component.
<code-example path="toh-pt6/src/app/hero-search.component.css">
</code-example>
@ -474,13 +495,15 @@ The `async` pipe subscribes to the `!{_Observable}` and produces the !{_array} o
Create the `HeroSearchComponent` class and metadata.
<code-example path="toh-pt6/src/app/hero-search.component.ts">
</code-example>
#### 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">
@ -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.
<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>
@ -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
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.
* `switchMap()` calls the search service for each search term that makes it through `debounce` and `distinctUntilChanged`.
* `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 `debounceTime()` and `distinctUntilChanged()`.
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:
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" linenums="false">
</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.
<code-example path="toh-pt6/src/app/dashboard.component.html" linenums="false">
</code-example>
@ -577,6 +603,7 @@ Finally, import `HeroSearchComponent` from
and add it to the `!{_declarations}` !{_array}.
<code-example path="toh-pt6/declFile" linenums="false" title="declFile (search)" region="search">
</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.
If you enter characters that match any existing hero names, you'll see something like this.
<figure class='image-display'>
<img src='assets/images/devguide/toh/toh-hero-search.png' alt="Hero Search Component"> </img>
</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:
<aio-filetree>
<aio-folder>
angular-tour-of-heroes
<aio-folder>
src
<aio-folder>
app
<aio-file>
app.component.ts
</aio-file>
<aio-file>
app.component.css
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
app-routing.module.ts
</aio-file>
<aio-file>
dashboard.component.css
</aio-file>
<aio-file>
dashboard.component.html
</aio-file>
<aio-file>
dashboard.component.ts
</aio-file>
<aio-file>
hero.ts
</aio-file>
<aio-file>
hero-detail.component.css
</aio-file>
<aio-file>
hero-detail.component.html
</aio-file>
<aio-file>
hero-detail.component.ts
</aio-file>
<aio-file>
hero-search.component.html (new)
</aio-file>
<aio-file>
hero-search.component.css (new)
</aio-file>
<aio-file>
hero-search.component.ts (new)
</aio-file>
<aio-file>
hero-search.service.ts (new)
</aio-file>
<aio-file>
hero.service.ts
</aio-file>
<aio-file>
heroes.component.css
</aio-file>
<aio-file>
heroes.component.html
</aio-file>
<aio-file>
heroes.component.ts
</aio-file>
<aio-file>
in-memory-data.service.ts (new)
</aio-file>
@ -706,26 +758,31 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
styles.css
</aio-file>
<aio-file>
systemjs.config.js
</aio-file>
<aio-file>
tsconfig.json
</aio-file>
@ -734,11 +791,13 @@ Verify that you have the following structure:
</aio-folder>
<aio-file>
node_modules ...
</aio-file>
<aio-file>
package.json
</aio-file>
@ -764,48 +823,58 @@ Here are the files you added or changed in this page.
<code-tabs>
<code-pane title="app.comp...ts" path="toh-6/src/app/app.component.ts">
</code-pane>
<code-pane title="app.mod...ts" path="toh-6/src/app/app.module.ts">
</code-pane>
<code-pane title="heroes.comp...ts" path="toh-6/src/app/heroes.component.ts">
</code-pane>
<code-pane title="heroes.comp...html" path="toh-6/src/app/heroes.component.html">
</code-pane>
<code-pane title="heroes.comp...css" path="toh-6/src/app/heroes.component.css">
</code-pane>
<code-pane title="hero-detail.comp...ts" path="toh-6/src/app/hero-detail.component.ts">
</code-pane>
<code-pane title="hero-detail.comp...html" path="toh-6/src/app/hero-detail.component.html">
</code-pane>
<code-pane title="hero.service.ts" path="toh-6/src/app/hero.service.ts">
</code-pane>
<code-pane title="in-memory-data.service.ts" path="toh-6/src/app/in-memory-data.service.ts">
</code-pane>
@ -815,23 +884,28 @@ Here are the files you added or changed in this page.
<code-tabs>
<code-pane title="hero-search.service.ts" path="toh-6/src/app/hero-search.service.ts">
</code-pane>
<code-pane title="hero-search.component.ts" path="toh-6/src/app/hero-search.component.ts">
</code-pane>
<code-pane title="hero-search.component.html" path="toh-6/src/app/hero-search.component.html">
</code-pane>
<code-pane title="hero-search.component.css" path="toh-6/src/app/hero-search.component.css">
</code-pane>