docs(aio): remove unnecessary blank lines

This commit is contained in:
Peter Bacon Darwin 2017-03-31 12:23:16 +01:00 committed by Pete Bacon Darwin
parent 0aa90c6be4
commit 46f8a6dd85
48 changed files with 1357 additions and 5587 deletions

File diff suppressed because it is too large Load Diff

View File

@ -66,14 +66,12 @@ Animations are defined inside `@Component` metadata. Before you can add animatio
to import a few animation-specific imports and functions: to import a few animation-specific imports and functions:
<code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false"> <code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false">
</code-example> </code-example>
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -83,7 +81,6 @@ metadata. It uses animations to transition between two states: `active` and `ina
hero is active, the element appears in a slightly larger size and lighter color. hero is active, the element appears in a slightly larger size and lighter color.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -102,7 +99,6 @@ Now, using the `[@triggerName]` syntax, attach the animation that you just defin
one or more elements in the component's template. one or more elements in the component's template.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -115,7 +111,6 @@ With this setup, an animated transition appears whenever a hero object changes s
Here's the full component implementation: Here's the full component implementation:
<code-example path="animations/src/app/hero-list-basic.component.ts"> <code-example path="animations/src/app/hero-list-basic.component.ts">
</code-example> </code-example>
@ -134,7 +129,6 @@ component's template.
You can define *styles* for each animation state: You can define *styles* for each animation state:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false">
</code-example> </code-example>
@ -147,7 +141,6 @@ After you define states, you can define *transitions* between the states. Each t
controls the timing of switching between one set of styles and the next: controls the timing of switching between one set of styles and the next:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -162,7 +155,6 @@ If several transitions have the same timing configuration, you can combine
them into the same `transition` definition: them into the same `transition` definition:
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -171,7 +163,6 @@ When both directions of a transition have the same timing, as in the previous
example, you can use the shorthand syntax `<=>`: example, you can use the shorthand syntax `<=>`:
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -183,7 +174,6 @@ When the transition finishes, none of these styles are kept because they're not
defined in a `state`. defined in a `state`.
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false"> <code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false">
</code-example> </code-example>
@ -230,10 +220,9 @@ entering and leaving of elements:
* Enter: `void => *` * Enter: `void => *`
* Leave: `* => void` * Leave: `* => void`
For example, in the `animations` !{_array} below there are two transitions that use For example, in the `animations` array below there are two transitions that use
the `void => *` and `* => void` syntax to animate the element in and out of the view. the `void => *` and `* => void` syntax to animate the element in and out of the view.
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -283,7 +272,6 @@ This gives you fine-grained control over each transition:
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -325,7 +313,6 @@ In this example, the leave animation takes whatever height the element has befor
leaves and animates from that height to zero: leaves and animates from that height to zero:
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -377,7 +364,6 @@ slight delay of 10 milliseconds as specified in `'0.2s 10 ease-out'`:
<code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -399,7 +385,6 @@ This example adds some "bounce" to the enter and leave animations with
keyframes: keyframes:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -429,7 +414,6 @@ enter and leave allows for two different timing configurations. Both
are applied to the same element in parallel, but run independently of each other: are applied to the same element in parallel, but run independently of each other:
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false"> <code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false">
</code-example> </code-example>
@ -443,7 +427,6 @@ In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
those callbacks like this: those callbacks like this:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false"> <code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false">
</code-example> </code-example>

View File

@ -11,6 +11,7 @@ during a build process.
{@a toc} {@a toc}
# Contents # Contents
- [Overview](guide/overview) - [Overview](guide/overview)
- [Ahead-of-time (AOT) vs just-in-time (JIT)](guide/aot-compiler#aot-jit) - [Ahead-of-time (AOT) vs just-in-time (JIT)](guide/aot-compiler#aot-jit)
- [Why do AOT compilation?](guide/aot-compiler#why-aot) - [Why do AOT compilation?](guide/aot-compiler#why-aot)
@ -114,21 +115,16 @@ Take the <a href='../guide/setup.html'>Setup</a> as a starting point.
A few minor changes to the lone `app.component` lead to these two class and HTML files: A few minor changes to the lone `app.component` lead to these two class and HTML files:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Install a few new npm dependencies with the following command: Install a few new npm dependencies with the following command:
@ -147,7 +143,6 @@ Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` on th
then modify it as follows. then modify it as follows.
<code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false"> <code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false">
</code-example> </code-example>
@ -241,21 +236,16 @@ Switch from the `platformBrowserDynamic.bootstrap` used in JIT compilation to
Here is AOT bootstrap in `main.ts` next to the original JIT version: Here is AOT bootstrap in `main.ts` next to the original JIT version:
<code-tabs> <code-tabs>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts"> <code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts"> <code-pane title="src/main-jit.ts" path="cb-aot-compiler/src/main-jit.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Be sure to [recompile](guide/aot-compiler#compiling-aot) with `ngc`! Be sure to [recompile](guide/aot-compiler#compiling-aot) with `ngc`!
@ -310,7 +300,6 @@ in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this. The cookbook configuration file looks like this.
<code-example path="cb-aot-compiler/rollup-config.js" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" linenums="false">
</code-example> </code-example>
@ -339,8 +328,7 @@ Luckily, there is a Rollup plugin that modifies _RxJs_
to use the ES `import` and `export` statements that Rollup requires. to use the ES `import` and `export` statements that Rollup requires.
Rollup then preserves the parts of `RxJS` referenced by the application Rollup then preserves the parts of `RxJS` referenced by the application
in the final bundle. Using it is straigthforward. Add the following to in the final bundle. Using it is straigthforward. Add the following to
the `plugins` !{_array} in `rollup-config.js`: the `plugins` array in `rollup-config.js`:
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false">
@ -351,8 +339,7 @@ the `plugins` !{_array} in `rollup-config.js`:
Rollup tree shaking reduces code size considerably. Minification makes it smaller still. Rollup tree shaking reduces code size considerably. Minification makes it smaller still.
This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code. This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code.
Add the following to the `plugins` !{_array}: Add the following to the `plugins` array:
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false"> <code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false">
@ -404,7 +391,6 @@ Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag: Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag:
<code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false"> <code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false">
</code-example> </code-example>
@ -431,45 +417,32 @@ The server starts, launches a browser, and the app should appear.
Here's the pertinent source code: Here's the pertinent source code:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-aot-compiler/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-aot-compiler/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts"> <code-pane title="src/main.ts" path="cb-aot-compiler/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/index.html" path="cb-aot-compiler/src/index.html"> <code-pane title="src/index.html" path="cb-aot-compiler/src/index.html">
</code-pane> </code-pane>
<code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json" path="cb-aot-compiler/tsconfig-aot.json">
</code-pane> </code-pane>
<code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js"> <code-pane title="rollup-config.js" path="cb-aot-compiler/rollup-config.js">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -501,7 +474,6 @@ The same source code can be built both ways. Here's one way to do that.
* Delete the script at the bottom of `index-jit.html` that loads `bundle.js` * Delete the script at the bottom of `index-jit.html` that loads `bundle.js`
* Restore the SystemJS scripts like this: * Restore the SystemJS scripts like this:
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false"> <code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false">
</code-example> </code-example>
@ -554,21 +526,16 @@ The JIT and AOT apps require their own `index.html` files because they setup and
Here they are for comparison: Here they are for comparison:
<code-tabs> <code-tabs>
<code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html"> <code-pane title="aot/index.html (AOT)" path="toh-6/aot/index.html">
</code-pane> </code-pane>
<code-pane title="src/index.html (JIT)" path="toh-6/src/index.html"> <code-pane title="src/index.html (JIT)" path="toh-6/src/index.html">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
The JIT version relies on `SystemJS` to load individual modules. The JIT version relies on `SystemJS` to load individual modules.
@ -584,21 +551,16 @@ The key differences, covered in the [Bootstrap](guide/aot-compiler#bootstrap) se
are evident in these `main` files which can and should reside in the same folder: are evident in these `main` files which can and should reside in the same folder:
<code-tabs> <code-tabs>
<code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts"> <code-pane title="main-aot.ts (AOT)" path="toh-6/src/main-aot.ts">
</code-pane> </code-pane>
<code-pane title="main.ts (JIT)" path="toh-6/src/main.ts"> <code-pane title="main.ts (JIT)" path="toh-6/src/main.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
***TypeScript configuration*** ***TypeScript configuration***
@ -610,21 +572,16 @@ AOT requires its own TypeScript configuration settings as well.
You'll need separate TypeScript configuration files such as these: You'll need separate TypeScript configuration files such as these:
<code-tabs> <code-tabs>
<code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json (AOT)" path="toh-6/tsconfig-aot.json">
</code-pane> </code-pane>
<code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json"> <code-pane title="src/tsconfig.json (JIT)" path="toh-6/src/tsconfig.1.json">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -655,7 +612,6 @@ Edit your `tsconfig-aot.json` to fit your project's file structure.
Rollup does the tree shaking as before. Rollup does the tree shaking as before.
<code-example path="toh-6/rollup-config.js" linenums="false"> <code-example path="toh-6/rollup-config.js" linenums="false">
</code-example> </code-example>
@ -682,33 +638,24 @@ Run the JIT-compiled app with `npm start` as for all other JIT examples.
Compiling with AOT presupposes certain supporting files, most of them discussed above. Compiling with AOT presupposes certain supporting files, most of them discussed above.
<code-tabs> <code-tabs>
<code-pane title="src/index.html" path="toh-6/src/index.html"> <code-pane title="src/index.html" path="toh-6/src/index.html">
</code-pane> </code-pane>
<code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js"> <code-pane title="copy-dist-files.js" path="toh-6/copy-dist-files.js">
</code-pane> </code-pane>
<code-pane title="rollup-config.js" path="toh-6/rollup-config.js"> <code-pane title="rollup-config.js" path="toh-6/rollup-config.js">
</code-pane> </code-pane>
<code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json"> <code-pane title="tsconfig-aot.json" path="toh-6/tsconfig-aot.json">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script: Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script:
@ -761,9 +708,7 @@ showing exactly which application and Angular modules and classes are included i
Here's the map for _Tour of Heroes_. Here's the map for _Tour of Heroes_.
<a href="assets/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image"> <a href="assets/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image">
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img> <img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img>
</figure> </figure>
</a> </a>

View File

@ -14,7 +14,6 @@ The [setup](guide/setup) instructions produce a new project with the following m
You'll evolve this module as your application grows. You'll evolve this module as your application grows.
<code-example path="setup/src/app/app.module.ts" linenums="false"> <code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example> </code-example>
@ -136,7 +135,6 @@ and you'll run it in a browser. You can learn about other options later.
The recommended place to bootstrap a JIT-compiled browser application is in a separate file The recommended place to bootstrap a JIT-compiled browser application is in a separate file
in the `src` folder named `src/main.ts` in the `src` folder named `src/main.ts`
<code-example path="setup/src/main.ts" linenums="false"> <code-example path="setup/src/main.ts" linenums="false">
</code-example> </code-example>
@ -151,7 +149,6 @@ creates an instance of the component and inserts it within the element tag ident
The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app` The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app`
so Angular looks for a `<my-app>` tag in the `index.html` like this one ... so Angular looks for a `<my-app>` tag in the `index.html` like this one ...
<code-example path="setup/src/index.html" region="my-app" linenums="false"> <code-example path="setup/src/index.html" region="my-app" linenums="false">
</code-example> </code-example>

View File

@ -6,7 +6,6 @@ The basic building blocks of Angular applications.
@description @description
Angular is a framework for building client applications in HTML and Angular is a framework for building client applications in HTML and
either JavaScript or a language like TypeScript that compiles to JavaScript. either JavaScript or a language like TypeScript that compiles to JavaScript.
@ -60,7 +59,6 @@ Learn these building blocks, and you're on your way.
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img> <img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
</figure> </figure>
Angular apps are modular and Angular has its own modularity system called _Angular modules_ or _NgModules_. Angular apps are modular and Angular has its own modularity system called _Angular modules_ or _NgModules_.
_Angular modules_ are a big deal. _Angular modules_ are a big deal.
@ -101,7 +99,6 @@ that hosts all other app views. Only the _root module_ should set this `bootstra
Here's a simple root module: Here's a simple root module:
<code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false">
</code-example> </code-example>
@ -118,7 +115,6 @@ Launch an application by _bootstrapping_ its root module.
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one. During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
<code-example path="architecture/src/main.ts" linenums="false"> <code-example path="architecture/src/main.ts" linenums="false">
</code-example> </code-example>
@ -135,14 +131,12 @@ The module declares some objects to be public by marking them with the `export`
Other JavaScript modules use *import statements* to access public objects from other modules. Other JavaScript modules use *import statements* to access public objects from other modules.
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false"> <code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false">
</code-example> </code-example>
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"> <code-example path="architecture/src/app/app.module.ts" region="export" linenums="false">
</code-example> </code-example>
@ -163,31 +157,27 @@ These are two different and _complementary_ module systems. Use them both to wri
<img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img> <img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
</figure> </figure>
Angular ships as a collection of JavaScript modules. You can think of them as library modules. Angular ships as a collection of JavaScript modules. You can think of them as library modules.
Each Angular library name begins with the `!{_at_angular}` prefix. Each Angular library name begins with the `@angular` prefix.
You install them with the **npm** package manager and import parts of them with JavaScript `import` statements. You install them with the **npm** package manager and import parts of them with JavaScript `import` statements.
<br class="l-clear-both"><br> <br class="l-clear-both"><br>
For example, import Angular's `Component` decorator from the `@angular/core` library like this: For example, import Angular's `Component` decorator from the `@angular/core` library like this:
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"> <code-example path="architecture/src/app/app.component.ts" region="import" linenums="false">
</code-example> </code-example>
You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements: You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false">
</code-example> </code-example>
In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this. In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"> <code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false">
</code-example> </code-example>
@ -231,12 +221,11 @@ You define a component's application logic&mdash;what it does to support the vie
The class interacts with the view through an API of properties and methods. The class interacts with the view through an API of properties and methods.
<a id="component-code"></a> <a id="component-code"></a>
For example, this `HeroListComponent` has a `heroes` property that returns !{_an} !{_array} of heroes For example, this `HeroListComponent` has a `heroes` property that returns an array of heroes
that it acquires from a service. that it acquires from a service.
`HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. `HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class">
</code-example> </code-example>
@ -263,7 +252,6 @@ A template looks like regular HTML, except for a few differences. Here is a
template for our `HeroListComponent`: template for our `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.html"> <code-example path="architecture/src/app/hero-list.component.html">
</code-example> </code-example>
@ -305,18 +293,16 @@ In fact, `HeroListComponent` really is *just a class*. It's not a component unti
To tell Angular that `HeroListComponent` is a component, attach **metadata** to the class. To tell Angular that `HeroListComponent` is a component, attach **metadata** to the class.
In !{_Lang}, you attach metadata by using !{_a} **!{_decorator}**. In TypeScript, you attach metadata by using a **decorator**.
Here's some metadata for `HeroListComponent`: Here's some metadata for `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata">
</code-example> </code-example>
Here is the `@Component` !{_decorator}, which identifies the class Here is the `@Component` decorator, which identifies the class
immediately below it as a component class. immediately below it as a component class.
The `@Component` decorator takes a required configuration object with the The `@Component` decorator takes a required configuration object with the
information Angular needs to create and present the component and its view. information Angular needs to create and present the component and its view.
@ -327,8 +313,7 @@ For example, if an app's HTML contains `<hero-list></hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags. Angular inserts an instance of the `HeroListComponent` view between those tags.
- `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates). - `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
- `providers`: array of **dependency injection providers** for services that the component requires.
- `providers`: !{_array} of **dependency injection providers** for services that the component requires.
This is one way to tell Angular that the component's constructor requires a `HeroService` This is one way to tell Angular that the component's constructor requires a `HeroService`
so it can get the list of heroes to display. so it can get the list of heroes to display.
@ -341,8 +326,8 @@ The metadata in the `@Component` tells Angular where to get the major building b
The template, metadata, and component together describe a view. The template, metadata, and component together describe a view.
Apply other metadata !{_decorator}s in a similar fashion to guide Angular behavior. Apply other metadata decorators in a similar fashion to guide Angular behavior.
`@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s.<br class="l-clear-both">The architectural takeaway is that you must add metadata to your code `@Injectable`, `@Input`, and `@Output` are a few of the more popular decorators.<br class="l-clear-both">The architectural takeaway is that you must add metadata to your code
so that Angular knows what to do. so that Angular knows what to do.
@ -367,7 +352,6 @@ Add binding markup to the template HTML to tell Angular how to connect both side
As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms: As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding"> <code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding">
</code-example> </code-example>
@ -385,7 +369,6 @@ that combines property and event binding in a single notation, using the `ngMode
Here's an example from the `HeroDetailComponent` template: Here's an example from the `HeroDetailComponent` template:
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"> <code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example> </code-example>
@ -425,9 +408,9 @@ Data binding is also important for communication between parent and child compon
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
according to the instructions given by **directives**. according to the instructions given by **directives**.
A directive is a class with a `@Directive` !{_decorator}. A directive is a class with a `@Directive` decorator.
A component is a *directive-with-a-template*; A component is a *directive-with-a-template*;
a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features. a `@Component` decorator is actually a `@Directive` decorator extended with template-oriented features.
<br class="l-clear-both"> <br class="l-clear-both">
@ -448,14 +431,12 @@ sometimes by name but more often as the target of an assignment or a binding.
The [example template](guide/architecture#templates) uses two built-in structural directives: The [example template](guide/architecture#templates) uses two built-in structural directives:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural"> <code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural">
</code-example> </code-example>
* [`*ngFor`](guide/displaying-data) tells Angular to stamp out one `<li>` per hero in the `heroes` list. * [`*ngFor`](guide/displaying-data) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data) includes the `HeroDetail` component only if a selected hero exists. * [`*ngIf`](guide/displaying-data) includes the `HeroDetail` component only if a selected hero exists.
**Attribute** directives alter the appearance or behavior of an existing element. **Attribute** directives alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name. In templates they look like regular HTML attributes, hence the name.
@ -465,7 +446,6 @@ an existing element (typically an `<input>`)
by setting its display value property and responding to change events. by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"> <code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel">
</code-example> </code-example>
@ -509,16 +489,14 @@ Yet services are fundamental to any Angular application. Components are big cons
Here's an example of a service class that logs to the browser console: Here's an example of a service class that logs to the browser console:
<code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class"> <code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class">
</code-example> </code-example>
Here's a `HeroService` that uses a !{_PromiseLinked} to fetch heroes. Here's a `HeroService` that uses a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to fetch heroes.
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work. The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class"> <code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class">
</code-example> </code-example>
@ -558,7 +536,6 @@ Angular uses dependency injection to provide new components with the services th
For example, the constructor of your `HeroListComponent` needs a `HeroService`: For example, the constructor of your `HeroListComponent` needs a `HeroService`:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor">
</code-example> </code-example>
@ -583,14 +560,12 @@ If the injector doesn't have a `HeroService`, how does it know how to make one?
In brief, you must have previously registered a **provider** of the `HeroService` with the injector. In brief, you must have previously registered a **provider** of the `HeroService` with the injector.
A provider is something that can create or return a service, typically the service class itself. A provider is something that can create or return a service, typically the service class itself.
You can register providers in modules or in components. You can register providers in modules or in components.
In general, add providers to the [root module](guide/architecture#module) so that In general, add providers to the [root module](guide/architecture#module) so that
the same instance of a service is available everywhere. the same instance of a service is available everywhere.
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers"> <code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers">
</code-example> </code-example>
@ -598,7 +573,6 @@ the same instance of a service is available everywhere.
Alternatively, register at a component level in the `providers` property of the `@Component` metadata: Alternatively, register at a component level in the `providers` property of the `@Component` metadata:
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"> <code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers">
</code-example> </code-example>
@ -671,6 +645,5 @@ by implementing the lifecycle hook interfaces.
> [**Router**](guide/router): Navigate from page to page within the client > [**Router**](guide/router): Navigate from page to page within the client
application and never leave the browser. application and never leave the browser.
> [**Testing**](guide/testing): Run unit tests on your application parts as they interact with the Angular framework > [**Testing**](guide/testing): Run unit tests on your application parts as they interact with the Angular framework
using the _Angular Testing Platform_. using the _Angular Testing Platform_.

View File

@ -51,7 +51,6 @@ directive to set an element's background color
when the user hovers over that element. You can apply it like this: when the user hovers over that element. You can apply it like this:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (applied)" region="applied">
</code-example> </code-example>
@ -59,12 +58,11 @@ when the user hovers over that element. You can apply it like this:
### Write the directive code ### Write the directive code
Follow the [setup](guide/setup) instructions for creating a new local project Follow the [setup](guide/setup) instructions for creating a new local project
named <span ngio-ex>attribute-directives</span>. named <code>attribute-directives</code>.
Create the following source file in the indicated folder: Create the following source file in the indicated folder:
<code-example path="attribute-directives/src/app/highlight.directive.1.ts"> <code-example path="attribute-directives/src/app/highlight.directive.1.ts">
</code-example> </code-example>
@ -104,7 +102,7 @@ For a simple demo, the short prefix, `my`, helps distinguish your custom directi
After the `@Directive` metadata comes the directive's controller class, After the `@Directive` metadata comes the directive's controller class,
called `HighlightDirective`, which contains the logic for the directive. called `HighlightDirective`, which contains the logic for the directive.
<span if-docs="ts">Exporting `HighlightDirective` makes it accessible to other components.</span> Exporting `HighlightDirective` makes it accessible to other components.
Angular creates a new instance of the directive's controller class for Angular creates a new instance of the directive's controller class for
each matching element, injecting an Angular `ElementRef` each matching element, injecting an Angular `ElementRef`
@ -118,11 +116,10 @@ To use the new `HighlightDirective`, create a template that
applies the directive as an attribute to a paragraph (`<p>`) element. applies the directive as an attribute to a paragraph (`<p>`) element.
In Angular terms, the `<p>` element is the attribute **host**. In Angular terms, the `<p>` element is the attribute **host**.
Put the template in its own <span ngio-ex>app.component.html</span> Put the template in its own <code>app.component.html</code>
file that looks like this: file that looks like this:
<code-example path="attribute-directives/src/app/app.component.1.html"> <code-example path="attribute-directives/src/app/app.component.1.html">
</code-example> </code-example>
@ -130,7 +127,6 @@ file that looks like this:
Now reference this template in the `AppComponent`: Now reference this template in the `AppComponent`:
<code-example path="attribute-directives/src/app/app.component.ts"> <code-example path="attribute-directives/src/app/app.component.ts">
</code-example> </code-example>
@ -140,7 +136,6 @@ add that class to the `declarations` NgModule metadata. This way Angular
recognizes the directive when it encounters `myHighlight` in the template. recognizes the directive when it encounters `myHighlight` in the template.
<code-example path="attribute-directives/src/app/app.module.ts"> <code-example path="attribute-directives/src/app/app.module.ts">
</code-example> </code-example>
@ -193,21 +188,19 @@ Begin by adding `HostListener` to the list of imported symbols;
add the `Input` symbol as well because you'll need it soon. add the `Input` symbol as well because you'll need it soon.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports">
</code-example> </code-example>
Then add two eventhandlers that respond when the mouse enters or leaves, Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` !{_decorator}. each adorned by the `HostListener` decorator.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods">
</code-example> </code-example>
The `@HostListener` !{_decorator} lets you subscribe to events of the DOM The `@HostListener` decorator lets you subscribe to events of the DOM
element that hosts an attribute directive, the `<p>` in this case. element that hosts an attribute directive, the `<p>` in this case.
@ -223,11 +216,10 @@ There are at least three problems with _that_ approach:
~~~ ~~~
The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`, The handlers delegate to a helper method that sets the color on the DOM element, `el`,
which you declare and initialize in the constructor. which you declare and initialize in the constructor.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor">
</code-example> </code-example>
@ -235,7 +227,6 @@ which you declare and initialize in the constructor.
Here's the updated directive in full: Here's the updated directive in full:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts">
</code-example> </code-example>
@ -257,7 +248,6 @@ In this section, you give the developer the power to set the highlight color whi
Start by adding a `highlightColor` property to the directive class like this: Start by adding a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color">
</code-example> </code-example>
@ -267,7 +257,7 @@ Start by adding a `highlightColor` property to the directive class like this:
{@a input} {@a input}
### Binding to an _@Input_ property ### Binding to an _@Input_ property
Notice the `@Input` !{_decorator}. It adds metadata to the class that makes the directive's `highlightColor` property available for binding. Notice the `@Input` decorator. It adds metadata to the class that makes the directive's `highlightColor` property available for binding.
It's called an *input* property because data flows from the binding expression _into_ the directive. It's called an *input* property because data flows from the binding expression _into_ the directive.
Without that input metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more about that. Without that input metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more about that.
@ -275,7 +265,6 @@ Without that input metadata, Angular rejects the binding; see [below](guide/attr
Try it by adding the following directive binding variations to the `AppComponent` template: Try it by adding the following directive binding variations to the `AppComponent` template:
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-1"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-1">
</code-example> </code-example>
@ -283,7 +272,6 @@ Try it by adding the following directive binding variations to the `AppComponent
Add a `color` property to the `AppComponent`. Add a `color` property to the `AppComponent`.
<code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example> </code-example>
@ -291,7 +279,6 @@ Add a `color` property to the `AppComponent`.
Let it control the highlight color with a property binding. Let it control the highlight color with a property binding.
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-2"> <code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" title="src/app/app.component.html (excerpt)" region="color-2">
</code-example> </code-example>
@ -299,7 +286,6 @@ Let it control the highlight color with a property binding.
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this. That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>
@ -312,7 +298,6 @@ That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name. You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2">
</code-example> </code-example>
@ -328,7 +313,6 @@ Fortunately you can name the directive property whatever you want _and_ **_alias
Restore the original property name and specify the selector as the alias in the argument to `@Input`. Restore the original property name and specify the selector as the alias in the argument to `@Input`.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color property with alias)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color property with alias)" region="color">
</code-example> </code-example>
@ -339,7 +323,6 @@ _Outside_ the directive, where you bind to it, it's known as `myHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want: You get the best of both worlds: the property name you want and the binding syntax you want:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>
@ -348,7 +331,6 @@ Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method
If someone neglects to bind to `highlightColor`, highlight in red: If someone neglects to bind to `highlightColor`, highlight in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"> <code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter">
</code-example> </code-example>
@ -356,7 +338,6 @@ If someone neglects to bind to `highlightColor`, highlight in red:
Here's the latest version of the directive class. Here's the latest version of the directive class.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (excerpt)"> <code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (excerpt)">
</code-example> </code-example>
@ -367,8 +348,7 @@ It may be difficult to imagine how this directive actually works.
In this section, you'll turn `AppComponent` into a harness that In this section, you'll turn `AppComponent` into a harness that
lets you pick the highlight color with a radio button and bind your color choice to the directive. lets you pick the highlight color with a radio button and bind your color choice to the directive.
Update <span ngio-ex>app.component.html</span> as follows: Update <code>app.component.html</code> as follows:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (v2)" region="v2"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (v2)" region="v2">
@ -378,7 +358,6 @@ Update <span ngio-ex>app.component.html</span> as follows:
Revise the `AppComponent.color` so that it has no initial value. Revise the `AppComponent.color` so that it has no initial value.
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="attribute-directives/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
</code-example> </code-example>
@ -402,7 +381,6 @@ Let the template developer set the default color.
Add a second **input** property to `HighlightDirective` called `defaultColor`: Add a second **input** property to `HighlightDirective` called `defaultColor`:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (defaultColor)" region="defaultColor">
</code-example> </code-example>
@ -411,7 +389,6 @@ Revise the directive's `onMouseEnter` so that it first tries to highlight with t
then with the `defaultColor`, and falls back to "red" if both properties are undefined. then with the `defaultColor`, and falls back to "red" if both properties are undefined.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter">
</code-example> </code-example>
@ -423,13 +400,12 @@ The developer should be able to write the following template HTML to both bind t
and fall back to "violet" as the default color. and fall back to "violet" as the default color.
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (defaultColor)" region="defaultColor"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (defaultColor)" region="defaultColor">
</code-example> </code-example>
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective` Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
because you made it _public_ with the `@Input` !{_decorator}. because you made it _public_ with the `@Input` decorator.
Here's how the harness should work when you're done coding. Here's how the harness should work when you're done coding.
@ -451,45 +427,32 @@ This page covered how to:
The final source code follows: The final source code follows:
<code-tabs> <code-tabs>
<code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts"> <code-pane title="app/app.component.ts" path="attribute-directives/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html"> <code-pane title="app/app.component.html" path="attribute-directives/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts"> <code-pane title="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts">
</code-pane> </code-pane>
<code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts"> <code-pane title="app/app.module.ts" path="attribute-directives/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="attribute-directives/src/main.ts"> <code-pane title="main.ts" path="attribute-directives/src/main.ts">
</code-pane> </code-pane>
<code-pane title="index.html" path="attribute-directives/src/index.html"> <code-pane title="index.html" path="attribute-directives/src/index.html">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
You can also experience and download the <live-example title="Attribute Directive example"></live-example>. You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
@ -500,7 +463,6 @@ In this demo, the `hightlightColor` property is an ***input*** property of
the `HighlightDirective`. You've seen it applied without an alias: the `HighlightDirective`. You've seen it applied without an alias:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color">
</code-example> </code-example>
@ -508,12 +470,11 @@ the `HighlightDirective`. You've seen it applied without an alias:
You've seen it with an alias: You've seen it with an alias:
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"> <code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color">
</code-example> </code-example>
Either way, the `@Input` !{_decorator} tells Angular that this property is Either way, the `@Input` decorator tells Angular that this property is
_public_ and available for binding by a parent component. _public_ and available for binding by a parent component.
Without `@Input`, Angular refuses to bind to the property. Without `@Input`, Angular refuses to bind to the property.
@ -524,35 +485,34 @@ The difference is a matter of trust.
Angular treats a component's template as _belonging_ to the component. Angular treats a component's template as _belonging_ to the component.
The component and its template trust each other implicitly. The component and its template trust each other implicitly.
Therefore, the component's own template may bind to _any_ property of that component, Therefore, the component's own template may bind to _any_ property of that component,
with or without the `@Input` !{_decorator}. with or without the `@Input` decorator.
But a component or directive shouldn't blindly trust _other_ components and directives. But a component or directive shouldn't blindly trust _other_ components and directives.
The properties of a component or directive are hidden from binding by default. The properties of a component or directive are hidden from binding by default.
They are _private_ from an Angular binding perspective. They are _private_ from an Angular binding perspective.
When adorned with the `@Input` !{_decorator}, the property becomes _public_ from an Angular binding perspective. When adorned with the `@Input` decorator, the property becomes _public_ from an Angular binding perspective.
Only then can it be bound by some other component or directive. Only then can it be bound by some other component or directive.
You can tell if `@Input` is needed by the position of the property name in a binding. You can tell if `@Input` is needed by the position of the property name in a binding.
* When it appears in the template expression to the ***right*** of the equals (=), * When it appears in the template expression to the ***right*** of the equals (=),
it belongs to the template's component and does not require the `@Input` !{_decorator}. it belongs to the template's component and does not require the `@Input` decorator.
* When it appears in **square brackets** ([ ]) to the **left** of the equals (=), * When it appears in **square brackets** ([ ]) to the **left** of the equals (=),
the property belongs to some _other_ component or directive; the property belongs to some _other_ component or directive;
that property must be adorned with the `@Input` !{_decorator}. that property must be adorned with the `@Input` decorator.
Now apply that reasoning to the following example: Now apply that reasoning to the following example:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"> <code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color">
</code-example> </code-example>
* The `color` property in the expression on the right belongs to the template's component. * The `color` property in the expression on the right belongs to the template's component.
The template and its component trust each other. The template and its component trust each other.
The `color` property doesn't require the `@Input` !{_decorator}. The `color` property doesn't require the `@Input` decorator.
* The `myHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`, * The `myHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`,
not a property of the template's component. There are trust issues. not a property of the template's component. There are trust issues.
Therefore, the directive property must carry the `@Input` !{_decorator}. Therefore, the directive property must carry the `@Input` decorator.

View File

@ -11,276 +11,186 @@ Angular supports most recent browsers. This includes the following specific vers
<table> <table>
<tr> <tr>
<th> <th>
Chrome Chrome
</th> </th>
<th> <th>
Firefox Firefox
</th> </th>
<th> <th>
Edge Edge
</th> </th>
<th> <th>
IE IE
</th> </th>
<th> <th>
Safari Safari
</th> </th>
<th> <th>
iOS iOS
</th> </th>
<th> <th>
Android Android
</th> </th>
<th> <th>
IE mobile IE mobile
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
latest latest
</td> </td>
<td> <td>
latest latest
</td> </td>
<td> <td>
14 14
</td> </td>
<td> <td>
11 11
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
Marshmallow (6.0) Marshmallow (6.0)
</td> </td>
<td> <td>
11 11
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
13 13
</td> </td>
<td> <td>
10 10
</td> </td>
<td> <td>
9 9
</td> </td>
<td> <td>
9 9
</td> </td>
<td> <td>
Lollipop<br>(5.0, 5.1) Lollipop<br>(5.0, 5.1)
</td> </td>
<td> <td>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>
</td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td>
</td>
<td> <td>
9 9
</td> </td>
<td> <td>
8 8
</td> </td>
<td> <td>
8 8
</td> </td>
<td> <td>
KitKat<br>(4.4) KitKat<br>(4.4)
</td> </td>
<td> <td>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>
</td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td> <td>
</td> </td>
<td>
</td>
<td> <td>
7 7
</td> </td>
<td> <td>
7 7
</td> </td>
<td> <td>
Jelly Bean<br>(4.1, 4.2, 4.3) Jelly Bean<br>(4.1, 4.2, 4.3)
</td> </td>
<td> <td>
</td> </td>
</tr> </tr>
</table> </table>
@ -301,7 +211,6 @@ Targeting such a wide range of browsers is challenging because they do not suppo
You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`) You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
that implement missing features in JavaScript. that implement missing features in JavaScript.
<code-example path="quickstart/src/index.html" region="polyfills" linenums="false"> <code-example path="quickstart/src/index.html" region="polyfills" linenums="false">
</code-example> </code-example>
@ -327,79 +236,55 @@ These are the polyfills required to run an Angular application on each supported
<table> <table>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<th> <th>
Browsers (desktop & mobile) Browsers (desktop & mobile)
</th> </th>
<th> <th>
Polyfills required Polyfills required
</th> </th>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
Chrome, Firefox, Edge, Safari 9+ Chrome, Firefox, Edge, Safari 9+
</td> </td>
<td> <td>
None None
</td> </td>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
Safari 7 & 8, IE10 & 11, Android 4.1+ Safari 7 & 8, IE10 & 11, Android 4.1+
</td> </td>
<td> <td>
[ES6](guide/browser-support#core-es6) [ES6](guide/browser-support#core-es6)
</td> </td>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
IE9 IE9
</td> </td>
<td> <td>
[ES6<br>classList](guide/browser-support#classlist) [ES6<br>classList](guide/browser-support#classlist)
</td> </td>
</tr> </tr>
</table> </table>
### Optional browser features to polyfill ## ### Optional browser features to polyfill ##
@ -413,126 +298,86 @@ Here are the features which may require additional polyfills:
<table> <table>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<th> <th>
Feature Feature
</th> </th>
<th> <th>
Polyfill Polyfill
</th> </th>
<th style="width: 50%"> <th style="width: 50%">
Browsers (desktop & mobile) Browsers (desktop & mobile)
</th> </th>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="./animations.html"> Animations </a> <a href="./animations.html"> Animations </a>
</td> </td>
<td> <td>
[Web Animations](guide/browser-support#web-animations) [Web Animations](guide/browser-support#web-animations)
</td> </td>
<td> <td>
All but Chrome and Firefox<br>Not supported in IE9 All but Chrome and Firefox<br>Not supported in IE9
</td> </td>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="../api/common/index/DatePipe-pipe.html" target="_blank"> Date </a> <span> , </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank"> currency </a> <span> , </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank"> decimal </a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank"> percent </a> <span> pipes </span> <a href="../api/common/index/DatePipe-pipe.html" target="_blank"> Date </a> <span> , </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank"> currency </a> <span> , </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank"> decimal </a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank"> percent </a> <span> pipes </span>
</td> </td>
<td> <td>
[Intl API](guide/browser-support#intl) [Intl API](guide/browser-support#intl)
</td> </td>
<td> <td>
All but Chrome, Firefox, Edge, IE11 and Safari 10 All but Chrome, Firefox, Edge, IE11 and Safari 10
</td> </td>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="../api/common/index/NgClass-directive.html" target="_blank"> NgClass </a> <span> on SVG elements </span> <a href="../api/common/index/NgClass-directive.html" target="_blank"> NgClass </a> <span> on SVG elements </span>
</td> </td>
<td> <td>
[classList](guide/browser-support#classlist) [classList](guide/browser-support#classlist)
</td> </td>
<td> <td>
IE10, IE11 IE10, IE11
</td> </td>
</tr> </tr>
<tr style="vertical-align: top"> <tr style="vertical-align: top">
<td> <td>
<a href="./server-communication.html"> Http </a> <span> when sending and receiving binary data </span> <a href="./server-communication.html"> Http </a> <span> when sending and receiving binary data </span>
</td> </td>
<td> <td>
[Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata) [Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
</td> </td>
<td> <td>
IE 9 IE 9
</td> </td>
</tr> </tr>
</table> </table>
### Suggested polyfills ## ### Suggested polyfills ##
@ -541,198 +386,134 @@ Below are the polyfills which are used to test the framework itself. They are a
<table> <table>
<tr> <tr>
<th> <th>
Polyfill Polyfill
</th> </th>
<th> <th>
License License
</th> </th>
<th> <th>
Size* Size*
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank"> ES6 </a> <a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank"> ES6 </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
27.4KB 27.4KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank"> classList </a> <a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank"> classList </a>
</td> </td>
<td> <td>
Public domain Public domain
</td> </td>
<td> <td>
1KB 1KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank"> Intl </a> <a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank"> Intl </a>
</td> </td>
<td> <td>
MIT / Unicode license MIT / Unicode license
</td> </td>
<td> <td>
13.5KB 13.5KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank"> Web Animations </a> <a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank"> Web Animations </a>
</td> </td>
<td> <td>
Apache Apache
</td> </td>
<td> <td>
14.8KB 14.8KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank"> Typed Array </a> <a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank"> Typed Array </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
4KB 4KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank"> Blob </a> <a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank"> Blob </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
1.3KB 1.3KB
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank"> FormData </a> <a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank"> FormData </a>
</td> </td>
<td> <td>
MIT MIT
</td> </td>
<td> <td>
0.4KB 0.4KB
</td> </td>
</tr> </tr>
</table> </table>
\* Figures are for minified and gzipped code, \* Figures are for minified and gzipped code,

View File

@ -24,6 +24,7 @@ In this cookbook we will explore many of the features of Dependency Injection (D
[Inject the component's DOM element](guide/cb-dependency-injection#component-element) [Inject the component's DOM element](guide/cb-dependency-injection#component-element)
[Define dependencies with providers](guide/cb-dependency-injection#providers) [Define dependencies with providers](guide/cb-dependency-injection#providers)
* [The *provide* object literal](guide/cb-dependency-injection#provide) * [The *provide* object literal](guide/cb-dependency-injection#provide)
* [useValue - the *value provider*](guide/cb-dependency-injection#usevalue) * [useValue - the *value provider*](guide/cb-dependency-injection#usevalue)
* [useClass - the *class provider*](guide/cb-dependency-injection#useclass) * [useClass - the *class provider*](guide/cb-dependency-injection#useclass)
@ -31,12 +32,14 @@ In this cookbook we will explore many of the features of Dependency Injection (D
* [useFactory - the *factory provider*](guide/cb-dependency-injection#usefactory) * [useFactory - the *factory provider*](guide/cb-dependency-injection#usefactory)
[Provider token alternatives](guide/cb-dependency-injection#tokens) [Provider token alternatives](guide/cb-dependency-injection#tokens)
* [class-interface](guide/cb-dependency-injection#class-interface) * [class-interface](guide/cb-dependency-injection#class-interface)
* [OpaqueToken](guide/cb-dependency-injection#opaque-token) * [OpaqueToken](guide/cb-dependency-injection#opaque-token)
[Inject into a derived class](guide/cb-dependency-injection#di-inheritance) [Inject into a derived class](guide/cb-dependency-injection#di-inheritance)
[Find a parent component by injection](guide/cb-dependency-injection#find-parent) [Find a parent component by injection](guide/cb-dependency-injection#find-parent)
* [Find parent with a known component type](guide/cb-dependency-injection#known-parent) * [Find parent with a known component type](guide/cb-dependency-injection#known-parent)
* [Cannot find a parent by its base class](guide/cb-dependency-injection#base-parent) * [Cannot find a parent by its base class](guide/cb-dependency-injection#base-parent)
* [Find a parent by its class-interface](guide/cb-dependency-injection#class-interface-parent) * [Find a parent by its class-interface](guide/cb-dependency-injection#class-interface-parent)
@ -55,7 +58,6 @@ In the following example, we import and register several services
in the `@Component` metadata `providers` array. in the `@Component` metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="import-services" linenums="false">
</code-example> </code-example>
@ -75,14 +77,12 @@ Learn more about providers [below](guide/cb-dependency-injection#providers).
Now that we've registered these services, Now that we've registered these services,
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -99,7 +99,6 @@ We see an example of the second case here, where we configure the Component Rout
in the `providers` list of the `AppModule`. in the `providers` list of the `AppModule`.
<code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.module.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -122,7 +121,6 @@ At each step, the consumer of dependencies simply declares what it requires in i
For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`.
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -131,7 +129,6 @@ The `UserContext` in turn has dependencies on both the `LoggerService` (again) a
a `UserService` that gathers information about a particular user. a `UserService` that gathers information about a particular user.
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectables" linenums="false">
</code-example> </code-example>
@ -154,7 +151,6 @@ Once all the dependencies are in place, the `AppComponent` displays the user inf
### *@Injectable()* ### *@Injectable()*
Notice the `@Injectable()`decorator on the `UserContextService` class. Notice the `@Injectable()`decorator on the `UserContextService` class.
<code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectable" linenums="false"> <code-example path="cb-dependency-injection/src/app/user-context.service.ts" region="injectable" linenums="false">
</code-example> </code-example>
@ -211,7 +207,6 @@ We can limit the scope of an injected service to a *branch* of the application h
by providing that service *at the sub-root component for that branch*. by providing that service *at the sub-root component for that branch*.
Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array: Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array:
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="injection"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="injection">
</code-example> </code-example>
@ -256,7 +251,6 @@ We call this *sandboxing* because each service and component instance has its ow
<a id="hero-bios-component"></a> <a id="hero-bios-component"></a>
Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="simple">
</code-example> </code-example>
@ -264,7 +258,6 @@ Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioCompo
Each `HeroBioComponent` can edit a single hero's biography. Each `HeroBioComponent` can edit a single hero's biography.
A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero. A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero.
<code-example path="cb-dependency-injection/src/app/hero-cache.service.ts" region="service"> <code-example path="cb-dependency-injection/src/app/hero-cache.service.ts" region="service">
</code-example> </code-example>
@ -275,7 +268,6 @@ They'd be competing with each other to determine which hero to cache.
Each `HeroBioComponent` gets its *own* `HeroCacheService` instance Each `HeroBioComponent` gets its *own* `HeroCacheService` instance
by listing the `HeroCacheService` in its metadata `providers` array. by listing the `HeroCacheService` in its metadata `providers` array.
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component"> <code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="component">
</code-example> </code-example>
@ -322,14 +314,12 @@ We look at this second, more interesting case in our next example.
### Demonstration ### Demonstration
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component). The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component).
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="hero-bios-and-contacts">
</code-example> </code-example>
Focus on the template: Focus on the template:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -338,7 +328,6 @@ We've inserted a `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template: placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
<code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="template" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bio.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -351,14 +340,12 @@ It looks like this, with the hero's telephone number from `HeroContactComponent`
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component">
</code-example> </code-example>
Focus on the constructor parameters Focus on the constructor parameters
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false">
</code-example> </code-example>
@ -408,7 +395,6 @@ require DOM access.
To illustrate, we've written a simplified version of the `HighlightDirective` from To illustrate, we've written a simplified version of the `HighlightDirective` from
the [Attribute Directives](guide/attribute-directives) chapter. the [Attribute Directives](guide/attribute-directives) chapter.
<code-example path="cb-dependency-injection/src/app/highlight.directive.ts"> <code-example path="cb-dependency-injection/src/app/highlight.directive.ts">
</code-example> </code-example>
@ -423,7 +409,6 @@ Its `nativeElement` property exposes the DOM element for the directive to manipu
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags, The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
first without a value (yielding the default color) and then with an assigned color value. first without a value (yielding the default color) and then with an assigned color value.
<code-example path="cb-dependency-injection/src/app/app.component.html" region="highlight" linenums="false"> <code-example path="cb-dependency-injection/src/app/app.component.html" region="highlight" linenums="false">
</code-example> </code-example>
@ -448,7 +433,6 @@ Angular passes this token to the injector and assigns the result to the paramete
Here's a typical example: Here's a typical example:
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -476,7 +460,6 @@ Angular initializes the injectors it creates with some providers it cares about.
We have to register our _own_ application providers manually, We have to register our _own_ application providers manually,
usually in the `providers` array of the `Component` or `Directive` metadata: usually in the `providers` array of the `Component` or `Directive` metadata:
<code-example path="cb-dependency-injection/src/app/app.component.ts" region="providers"> <code-example path="cb-dependency-injection/src/app/app.component.ts" region="providers">
</code-example> </code-example>
@ -486,7 +469,6 @@ usually in the `providers` array of the `Component` or `Directive` metadata:
The simple class provider is the most typical by far. The simple class provider is the most typical by far.
We mention the class in the `providers` array and we're done. We mention the class in the `providers` array and we're done.
<code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="class-provider" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-bios.component.ts" region="class-provider" linenums="false">
</code-example> </code-example>
@ -504,7 +486,6 @@ The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about. It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="hero-of-the-month">
</code-example> </code-example>
@ -535,7 +516,6 @@ The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class; The first provides an instance of the `Hero` class;
the second specifies a literal string resource: the second specifies a literal string resource:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-value" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-value" linenums="false">
</code-example> </code-example>
@ -551,7 +531,6 @@ The value of a *value provider* must be defined *now*. We can't create the value
Obviously the title string literal is immediately available. Obviously the title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file: The `someHero` variable in this example was set earlier in the file:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="some-hero"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="some-hero">
</code-example> </code-example>
@ -571,7 +550,6 @@ or fake the behavior of the real class in a test case.
We see two examples in the `HeroOfTheMonthComponent`: We see two examples in the `HeroOfTheMonthComponent`:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-class" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-class" linenums="false">
</code-example> </code-example>
@ -593,7 +571,6 @@ Components outside the tree continue to receive the original `LoggerService` ins
The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false">
</code-example> </code-example>
@ -608,7 +585,6 @@ The `useExisting` provider maps one token to another.
In effect, the first token is an ***alias*** for the service associated with second token, In effect, the first token is an ***alias*** for the service associated with second token,
creating ***two ways to access the same service object***. creating ***two ways to access the same service object***.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example> </code-example>
@ -619,7 +595,6 @@ Imagine that the `LoggerService` had a large API (it's actually only three metho
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface): We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface):
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example> </code-example>
@ -647,7 +622,6 @@ The following image, which displays the logging date, confirms the point:
The `useFactory` provider creates a dependency object by calling a factory function The `useFactory` provider creates a dependency object by calling a factory function
as seen in this example. as seen in this example.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-factory">
</code-example> </code-example>
@ -666,7 +640,6 @@ The `runnersUpFactory` itself isn't the provider factory function.
The true provider factory function is the function that `runnersUpFactory` returns. The true provider factory function is the function that `runnersUpFactory` returns.
<code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false"> <code-example path="cb-dependency-injection/src/app/runners-up.ts" region="factory-synopsis" linenums="false">
</code-example> </code-example>
@ -710,14 +683,12 @@ That's the subject of our next section.
In the previous *Hero of the Month* example, we used the `MinimalLogger` class In the previous *Hero of the Month* example, we used the `MinimalLogger` class
as the token for a provider of a `LoggerService`. as the token for a provider of a `LoggerService`.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="use-existing">
</code-example> </code-example>
The `MinimalLogger` is an abstract class. The `MinimalLogger` is an abstract class.
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger" linenums="false">
</code-example> </code-example>
@ -728,7 +699,6 @@ Instead, we use it like an interface.
Look again at the declaration for `DateLoggerService` Look again at the declaration for `DateLoggerService`
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service-signature" linenums="false">
</code-example> </code-example>
@ -761,7 +731,6 @@ Using a class as an interface gives us the characteristics of an interface in a
The minimize memory cost, the class should have *no implementation*. The minimize memory cost, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger-transpiled" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="minimal-logger-transpiled" linenums="false">
</code-example> </code-example>
@ -788,14 +757,12 @@ The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example, We encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider. in the *title* value provider and in the *runnersUp* factory provider.
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="provide-opaque-token" linenums="false">
</code-example> </code-example>
We created the `TITLE` token like this: We created the `TITLE` token like this:
<code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-of-the-month.component.ts" region="opaque-token" linenums="false">
</code-example> </code-example>
@ -823,7 +790,6 @@ It demands its own instance of the `HeroService` to get heroes
and displays them in the order they arrive from the database. and displays them in the order they arrive from the database.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="heroes-base">
</code-example> </code-example>
@ -851,7 +817,6 @@ We must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor. then pass it down to the base class inside the constructor.
<code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes"> <code-example path="cb-dependency-injection/src/app/sorted-heroes.component.ts" region="sorted-heroes">
</code-example> </code-example>
@ -898,7 +863,6 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex} {@a alex}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-1" linenums="false">
</code-example> </code-example>
@ -906,7 +870,6 @@ In the following example, the parent `AlexComponent` has several children includ
*Cathy* reports whether or not she has access to *Alex* *Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor: after injecting an `AlexComponent` into her constructor:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="cathy" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="cathy" linenums="false">
</code-example> </code-example>
@ -944,14 +907,12 @@ We are asking *can a component inject its parent via the parent's base class*?
The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex) The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex)
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`. we see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example> </code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="craig" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="craig" linenums="false">
</code-example> </code-example>
@ -978,7 +939,6 @@ and add that provider to the `providers` array of the `@Component` metadata for
{@a alex-providers} {@a alex-providers}
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>
@ -988,7 +948,6 @@ The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-class" linenums="false">
</code-example> </code-example>
@ -1013,7 +972,6 @@ That means he must both *inject* the `Parent` *class-interface* to get *Alice* a
Here's *Barry*: Here's *Barry*:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry" linenums="false">
</code-example> </code-example>
@ -1023,21 +981,16 @@ If we're going to keep writing [*alias providers*](guide/cb-dependency-injection
For now, focus on *Barry*'s constructor: For now, focus on *Barry*'s constructor:
<code-tabs> <code-tabs>
<code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor"> <code-pane title="Barry's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="barry-ctor">
</code-pane> </code-pane>
<code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor"> <code-pane title="Carol's constructor" path="cb-dependency-injection/src/app/parent-finder.component.ts" region="carol-ctor">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator.
@ -1066,7 +1019,6 @@ We [learned earlier](guide/cb-dependency-injection#class-interface) that a *clas
Our example defines a `Parent` *class-interface* . Our example defines a `Parent` *class-interface* .
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="parent" linenums="false">
</code-example> </code-example>
@ -1077,7 +1029,6 @@ Such a narrowing interface helps decouple the child component class from its par
A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-class-signature" linenums="false">
</code-example> </code-example>
@ -1086,7 +1037,6 @@ Doing so adds clarity to the code. But it's not technically necessary.
Although the `AlexComponent` has a `name` property (as required by its `Base` class) Although the `AlexComponent` has a `name` property (as required by its `Base` class)
its class signature doesn't mention `Parent`: its class signature doesn't mention `Parent`:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-class-signature" linenums="false">
</code-example> </code-example>
@ -1109,21 +1059,18 @@ It doesn't in this example *only* to demonstrate that the code will compile and
Writing variations of the same parent *alias provider* gets old quickly, Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref): especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref):
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>
We can extract that logic into a helper function like this: We can extract that logic into a helper function like this:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-the-parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-the-parent" linenums="false">
</code-example> </code-example>
Now we can add a simpler, more meaningful parent provider to our components: Now we can add a simpler, more meaningful parent provider to our components:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alice-providers" linenums="false">
</code-example> </code-example>
@ -1133,14 +1080,12 @@ Our application might have a variety of parent types, each with its own *class-i
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="provide-parent" linenums="false">
</code-example> </code-example>
And here's how we could use it with a different parent type: And here's how we could use it with a different parent type:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="beth-providers" linenums="false">
</code-example> </code-example>
@ -1169,7 +1114,6 @@ appear *above* the class definition.
We break the circularity with `forwardRef`: We break the circularity with `forwardRef`:
<code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false"> <code-example path="cb-dependency-injection/src/app/parent-finder.component.ts" region="alex-providers" linenums="false">
</code-example> </code-example>

View File

@ -118,7 +118,6 @@ You can find it in `./src/app/app.component.ts`.
Open the component file and change the `title` property from _app works!_ to _My First Angular App_: Open the component file and change the `title` property from _app works!_ to _My First Angular App_:
<code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false"> <code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false">
</code-example> </code-example>
@ -128,7 +127,6 @@ The browser reloads automatically with the revised title. That's nice, but it co
Open `src/app/app.component.css` and give the component some style. Open `src/app/app.component.css` and give the component some style.
<code-example path="cli-quickstart/src/app/app.component.css" linenums="false"> <code-example path="cli-quickstart/src/app/app.component.css" linenums="false">
</code-example> </code-example>
@ -169,126 +167,86 @@ Any files outside of this folder are meant to support building your app.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.css app.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.html app.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.spec.ts app.component.spec.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-folder> <aio-folder>
assets assets
<aio-file> <aio-file>
.gitkeep .gitkeep
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-folder> <aio-folder>
environments environments
<aio-file> <aio-file>
environment.prod.ts environment.prod.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
environment.ts environment.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
favicon.ico favicon.ico
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
polyfills.ts polyfills.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
test.ts test.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.app.json tsconfig.app.json
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.spec.json tsconfig.spec.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
@ -301,105 +259,73 @@ Any files outside of this folder are meant to support building your app.
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>app/app.component.{ts,html,css,spec.ts}</code> <code>app/app.component.{ts,html,css,spec.ts}</code>
</td> </td>
<td> <td>
Defines the `AppComponent` along with an HTML template, CSS stylesheet, and a unit test. Defines the `AppComponent` along with an HTML template, CSS stylesheet, and a unit test.
It is the **root** component of what will become a tree of nested components It is the **root** component of what will become a tree of nested components
as the application evolves. as the application evolves.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>app/app.module.ts</code> <code>app/app.module.ts</code>
</td> </td>
<td> <td>
Defines `AppModule`, the [root module](guide/guide/appmodule) that tells Angular how to assemble the application. Defines `AppModule`, the [root module](guide/guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`. Right now it declares only the `AppComponent`.
Soon there will be more components to declare. Soon there will be more components to declare.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>assets/*</code> <code>assets/*</code>
</td> </td>
<td> <td>
A folder where you can put images and anything else to be copied wholesale A folder where you can put images and anything else to be copied wholesale
when you build your application. when you build your application.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>environments/*</code> <code>environments/*</code>
</td> </td>
<td> <td>
This folder contains one file for each of your destination environments, This folder contains one file for each of your destination environments,
each exporting simple configuration variables to use in your application. each exporting simple configuration variables to use in your application.
@ -410,39 +336,27 @@ Any files outside of this folder are meant to support building your app.
Either way, the CLI has you covered. Either way, the CLI has you covered.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>favicon.ico</code> <code>favicon.ico</code>
</td> </td>
<td> <td>
Every site wants to look good on the bookmark bar. Every site wants to look good on the bookmark bar.
Get started with your very own Angular icon. Get started with your very own Angular icon.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>index.html</code> <code>index.html</code>
</td> </td>
<td> <td>
The main HTML page that is served when someone visits your site. The main HTML page that is served when someone visits your site.
Most of the time you'll never need to edit it. Most of the time you'll never need to edit it.
@ -450,20 +364,14 @@ Any files outside of this folder are meant to support building your app.
never need to add any `<script>` or `<link>` tags here manually. never need to add any `<script>` or `<link>` tags here manually.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>main.ts</code> <code>main.ts</code>
</td> </td>
<td> <td>
The main entry point for your app. The main entry point for your app.
Compiles the application with the [JIT compiler](guide/glossary) Compiles the application with the [JIT compiler](guide/glossary)
@ -472,20 +380,14 @@ Any files outside of this folder are meant to support building your app.
without changing any code by passing in `--aot` to `ng build` or `ng serve`. without changing any code by passing in `--aot` to `ng build` or `ng serve`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>polyfills.ts</code> <code>polyfills.ts</code>
</td> </td>
<td> <td>
Different browsers have different levels of support of the web standards. Different browsers have different levels of support of the web standards.
Polyfills help normalize those differences. Polyfills help normalize those differences.
@ -493,60 +395,42 @@ Any files outside of this folder are meant to support building your app.
the [Browser Support guide](guide/guide/browser-support) for more information. the [Browser Support guide](guide/guide/browser-support) for more information.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>styles.css</code> <code>styles.css</code>
</td> </td>
<td> <td>
Your global styles go here. Your global styles go here.
Most of the time you'll want to have local styles in your components for easier maintenance, Most of the time you'll want to have local styles in your components for easier maintenance,
but styles that affect all of your app need to be in a central place. but styles that affect all of your app need to be in a central place.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>test.ts</code> <code>test.ts</code>
</td> </td>
<td> <td>
This is the main entry point for your unit tests. This is the main entry point for your unit tests.
It has some custom configuration that might be unfamiliar, but it's not something you'll It has some custom configuration that might be unfamiliar, but it's not something you'll
need to edit. need to edit.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.{app|spec}.json</code> <code>tsconfig.{app|spec}.json</code>
</td> </td>
<td> <td>
TypeScript compiler configuration for the Angular app (`tsconfig.app.json`) TypeScript compiler configuration for the Angular app (`tsconfig.app.json`)
and for the unit tests (`tsconfig.spec.json`). and for the unit tests (`tsconfig.spec.json`).
@ -554,10 +438,8 @@ Any files outside of this folder are meant to support building your app.
</td> </td>
</tr> </tr>
</table> </table>
@ -569,102 +451,70 @@ These files go in the root folder next to `src/`.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
my-app my-app
<aio-folder> <aio-folder>
e2e e2e
<aio-file> <aio-file>
app.e2e-spec.ts app.e2e-spec.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.po.ts app.po.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.e2e.json tsconfig.e2e.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules/... node_modules/...
</aio-file> </aio-file>
<aio-file> <aio-file>
src/... src/...
</aio-file> </aio-file>
<aio-file> <aio-file>
.angular-cli.json .angular-cli.json
</aio-file> </aio-file>
<aio-file> <aio-file>
.editorconfig .editorconfig
</aio-file> </aio-file>
<aio-file> <aio-file>
.gitignore .gitignore
</aio-file> </aio-file>
<aio-file> <aio-file>
karma.conf.js karma.conf.js
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
<aio-file> <aio-file>
protractor.conf.js protractor.conf.js
</aio-file> </aio-file>
<aio-file> <aio-file>
README.md README.md
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
<aio-file> <aio-file>
tslint.json tslint.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
@ -677,46 +527,32 @@ These files go in the root folder next to `src/`.
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>e2e/</code> <code>e2e/</code>
</td> </td>
<td> <td>
Inside `e2e/` live the End-to-End tests. Inside `e2e/` live the End-to-End tests.
They shouldn't be inside `src/` because e2e tests are really a separate app that They shouldn't be inside `src/` because e2e tests are really a separate app that
@ -724,39 +560,27 @@ These files go in the root folder next to `src/`.
That's also why they have their own `tsconfig.e2e.json`. That's also why they have their own `tsconfig.e2e.json`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>node_modules/</code> <code>node_modules/</code>
</td> </td>
<td> <td>
`Node.js` creates this folder and puts all third party modules listed in `Node.js` creates this folder and puts all third party modules listed in
`package.json` inside of it. `package.json` inside of it.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.angular-cli.json</code> <code>.angular-cli.json</code>
</td> </td>
<td> <td>
Configuration for Angular CLI. Configuration for Angular CLI.
In this file you can set several defaults and also configure what files are included In this file you can set several defaults and also configure what files are included
@ -764,20 +588,14 @@ These files go in the root folder next to `src/`.
Check out the official documentation if you want to know more. Check out the official documentation if you want to know more.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.editorconfig</code> <code>.editorconfig</code>
</td> </td>
<td> <td>
Simple configuration for your editor to make sure everyone that uses your project Simple configuration for your editor to make sure everyone that uses your project
has the same basic configuration. has the same basic configuration.
@ -785,133 +603,91 @@ These files go in the root folder next to `src/`.
See http://editorconfig.org for more information. See http://editorconfig.org for more information.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>.gitignore</code> <code>.gitignore</code>
</td> </td>
<td> <td>
Git configuration to make sure autogenerated files are not commited to source control. Git configuration to make sure autogenerated files are not commited to source control.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma.conf.js</code> <code>karma.conf.js</code>
</td> </td>
<td> <td>
Unit test configuration for the [Karma test runner](https://karma-runner.github.io), Unit test configuration for the [Karma test runner](https://karma-runner.github.io),
used when running `ng test`. used when running `ng test`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>package.json</code> <code>package.json</code>
</td> </td>
<td> <td>
`npm` configuration listing the third party packages your project uses. `npm` configuration listing the third party packages your project uses.
You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here. You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>protractor.conf.js</code> <code>protractor.conf.js</code>
</td> </td>
<td> <td>
End-to-end test configuration for [Protractor](http://www.protractortest.org/), End-to-end test configuration for [Protractor](http://www.protractortest.org/),
used when running `ng e2e`. used when running `ng e2e`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>README.md</code> <code>README.md</code>
</td> </td>
<td> <td>
Basic documentation for your project, pre-filled with CLI command information. Basic documentation for your project, pre-filled with CLI command information.
Make sure to enhance it with project documentation so that anyone Make sure to enhance it with project documentation so that anyone
checking out the repo can build your app! checking out the repo can build your app!
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.json</code> <code>tsconfig.json</code>
</td> </td>
<td> <td>
TypeScript compiler configuration for your IDE to pick up and give you helpful tooling. TypeScript compiler configuration for your IDE to pick up and give you helpful tooling.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tslint.json</code> <code>tslint.json</code>
</td> </td>
<td> <td>
Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with
[Codelyzer](http://codelyzer.com/), used when running `ng lint`. [Codelyzer](http://codelyzer.com/), used when running `ng lint`.
@ -919,10 +695,8 @@ These files go in the root folder next to `src/`.
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -23,7 +23,6 @@ in which two or more components share information.
typically adorned with [@Input decorations](guide/template-syntax). typically adorned with [@Input decorations](guide/template-syntax).
<code-example path="cb-component-communication/src/app/hero-child.component.ts"> <code-example path="cb-component-communication/src/app/hero-child.component.ts">
</code-example> </code-example>
@ -35,7 +34,6 @@ binding its `master` string property to the child's `master` alias,
and each iteration's `hero` instance to the child's `hero` property. and each iteration's `hero` instance to the child's `hero` property.
<code-example path="cb-component-communication/src/app/hero-parent.component.ts"> <code-example path="cb-component-communication/src/app/hero-parent.component.ts">
</code-example> </code-example>
@ -52,7 +50,6 @@ The running application displays three heroes:
E2E test that all children were instantiated and displayed as expected: E2E test that all children were instantiated and displayed as expected:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child">
</code-example> </code-example>
@ -67,7 +64,6 @@ The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text. trims the whitespace from a name and replaces an empty value with default text.
<code-example path="cb-component-communication/src/app/name-child.component.ts"> <code-example path="cb-component-communication/src/app/name-child.component.ts">
</code-example> </code-example>
@ -75,7 +71,6 @@ trims the whitespace from a name and replaces an empty value with default text.
Here's the `NameParentComponent` demonstrating name variations including a name with all spaces: Here's the `NameParentComponent` demonstrating name variations including a name with all spaces:
<code-example path="cb-component-communication/src/app/name-parent.component.ts"> <code-example path="cb-component-communication/src/app/name-parent.component.ts">
</code-example> </code-example>
@ -91,7 +86,6 @@ Here's the `NameParentComponent` demonstrating name variations including a name
E2E tests of input property setter with empty and non-empty names: E2E tests of input property setter with empty and non-empty names:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter">
</code-example> </code-example>
@ -113,7 +107,6 @@ Learn about `ngOnChanges()` in the [LifeCycle Hooks](guide/lifecycle-hooks) chap
This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes: This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
<code-example path="cb-component-communication/src/app/version-child.component.ts"> <code-example path="cb-component-communication/src/app/version-child.component.ts">
</code-example> </code-example>
@ -121,7 +114,6 @@ This `VersionChildComponent` detects changes to the `major` and `minor` input pr
The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them. The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them.
<code-example path="cb-component-communication/src/app/version-parent.component.ts"> <code-example path="cb-component-communication/src/app/version-parent.component.ts">
</code-example> </code-example>
@ -139,7 +131,6 @@ Test that ***both*** input properties are set initially and that button clicks t
the expected `ngOnChanges` calls and values: the expected `ngOnChanges` calls and values:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges"> <code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges">
</code-example> </code-example>
@ -156,7 +147,6 @@ The child's `EventEmitter` property is an ***output property***,
as seen in this `VoterComponent`: as seen in this `VoterComponent`:
<code-example path="cb-component-communication/src/app/voter.component.ts"> <code-example path="cb-component-communication/src/app/voter.component.ts">
</code-example> </code-example>
@ -167,7 +157,6 @@ The parent `VoteTakerComponent` binds an event handler called `onVoted()` that r
payload `$event` and updates a counter. payload `$event` and updates a counter.
<code-example path="cb-component-communication/src/app/votetaker.component.ts"> <code-example path="cb-component-communication/src/app/votetaker.component.ts">
</code-example> </code-example>
@ -185,7 +174,6 @@ and the method processes it:
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters: Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
<code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent"> <code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent">
</code-example> </code-example>
@ -205,7 +193,6 @@ The following is a child `CountdownTimerComponent` that repeatedly counts down t
It has `start` and `stop` methods that control the clock and it displays a It has `start` and `stop` methods that control the clock and it displays a
countdown status message in its own template. countdown status message in its own template.
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts"> <code-example path="cb-component-communication/src/app/countdown-timer.component.ts">
</code-example> </code-example>
@ -213,7 +200,6 @@ countdown status message in its own template.
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows: The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv"> <code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv">
</code-example> </code-example>
@ -245,7 +231,6 @@ match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer: Test also that clicking the *Stop* button pauses the countdown timer:
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests"> <code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests">
</code-example> </code-example>
@ -278,7 +263,6 @@ is solely for the purpose of demonstration.
Here is the parent, `CountdownViewChildParentComponent`: Here is the parent, `CountdownViewChildParentComponent`:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc"> <code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc">
</code-example> </code-example>
@ -322,7 +306,6 @@ Components outside this component subtree have no access to the service or their
This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children. This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children.
<code-example path="cb-component-communication/src/app/mission.service.ts"> <code-example path="cb-component-communication/src/app/mission.service.ts">
</code-example> </code-example>
@ -331,7 +314,6 @@ The `MissionControlComponent` both provides the instance of the service that it
(through the `providers` metadata array) and injects that instance into itself through its constructor: (through the `providers` metadata array) and injects that instance into itself through its constructor:
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts"> <code-example path="cb-component-communication/src/app/missioncontrol.component.ts">
</code-example> </code-example>
@ -340,7 +322,6 @@ The `AstronautComponent` also injects the service in its constructor.
Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance: Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance:
<code-example path="cb-component-communication/src/app/astronaut.component.ts"> <code-example path="cb-component-communication/src/app/astronaut.component.ts">
</code-example> </code-example>
@ -374,7 +355,6 @@ Tests click buttons of both the parent `MissionControlComponent` and the `Astron
and verify that the history meets expectations: and verify that the history meets expectations:
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service"> <code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service">
</code-example> </code-example>

View File

@ -33,11 +33,10 @@ but also the CSS styles that go with that template,
specifying any selectors, rules, and media queries that you need. specifying any selectors, rules, and media queries that you need.
One way to do this is to set the `styles` property in the component metadata. One way to do this is to set the `styles` property in the component metadata.
The `styles` property takes #{_an} #{_array} of strings that contain CSS code. The `styles` property takes an array of strings that contain CSS code.
Usually you give it one string, as in the following example: Usually you give it one string, as in the following example:
<code-example path="component-styles/src/app/hero-app.component.ts" linenums="false"> <code-example path="component-styles/src/app/hero-app.component.ts" linenums="false">
</code-example> </code-example>
@ -74,7 +73,6 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template). targeting elements *inside* the component's template).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false">
</code-example> </code-example>
@ -89,7 +87,6 @@ including another selector inside parentheses after `:host`.
The next example targets the host element again, but only when it also has the `active` CSS class. The next example targets the host element again, but only when it also has the `active` CSS class.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false">
</code-example> </code-example>
@ -108,7 +105,6 @@ The following example applies a `background-color` style to all `<h2>` elements
if some ancestor element has the CSS class `theme-light`. if some ancestor element has the CSS class `theme-light`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false">
</code-example> </code-example>
@ -124,7 +120,6 @@ children and content children of the component.
The following example targets all `<h3>` elements, from the host element down The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM. through this component to all of its child elements in the DOM.
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false"> <code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false">
</code-example> </code-example>
@ -156,9 +151,8 @@ The scoping rules outlined earlier apply to each of these loading patterns.
### Styles in metadata ### Styles in metadata
You can add a `styles` #{_array} property to the `@Component` #{_decorator}. You can add a `styles` array property to the `@Component` decorator.
Each string in the #{_array} (usually just one string) defines the CSS. Each string in the array (usually just one string) defines the CSS.
<code-example path="component-styles/src/app/hero-app.component.ts"> <code-example path="component-styles/src/app/hero-app.component.ts">
@ -168,8 +162,7 @@ Each string in the #{_array} (usually just one string) defines the CSS.
### Style URLs in metadata ### Style URLs in metadata
You can load styles from external CSS files by adding a `styleUrls` attribute You can load styles from external CSS files by adding a `styleUrls` attribute
into a component's `@Component` #{_decorator}: into a component's `@Component` decorator:
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls"> <code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls">
@ -178,7 +171,6 @@ into a component's `@Component` #{_decorator}:
~~~ {.alert.is-important} ~~~ {.alert.is-important}
The URL is relative to the *application root*, which is usually the The URL is relative to the *application root*, which is usually the
@ -192,7 +184,6 @@ To specify a URL relative to the component file, see [Appendix 2](guide/componen
~~~ {.l-sub-section} ~~~ {.l-sub-section}
If you use module bundlers like Webpack, you can also use the `styles` attribute If you use module bundlers like Webpack, you can also use the `styles` attribute
@ -215,7 +206,6 @@ You can embed styles directly into the HTML template by putting them
inside `<style>` tags. inside `<style>` tags.
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles"> <code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles">
</code-example> </code-example>
@ -228,7 +218,6 @@ As with `styleUrls`, the link tag's `href` URL is relative to the
application root, not the component file. application root, not the component file.
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink"> <code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink">
</code-example> </code-example>
@ -238,11 +227,9 @@ application root, not the component file.
You can also import CSS files into the CSS files using the standard CSS `@import` rule. You can also import CSS files into the CSS files using the standard CSS `@import` rule.
For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import) For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import)
on the [MDN](https://developer.mozilla.org) site. on the [MDN](https://developer.mozilla.org) site.
In this case, the URL is relative to the CSS file into which you're importing. In this case, the URL is relative to the CSS file into which you're importing.
<code-example path="component-styles/src/app/hero-details.component.css" region="import"> <code-example path="component-styles/src/app/hero-details.component.css" region="import">
</code-example> </code-example>
@ -276,7 +263,6 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata: To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false"> <code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false">
</code-example> </code-example>
@ -355,11 +341,9 @@ It's common practice to split a component's code, HTML, and CSS into three separ
You include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively. You include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
Because these files are co-located with the component, Because these files are co-located with the component,
it would be nice to refer to them by name without also having to specify a path back to the root of the application. it would be nice to refer to them by name without also having to specify a path back to the root of the application.
You can use a relative URL by prefixing your filenames with `./`: You can use a relative URL by prefixing your filenames with `./`:
<code-example path="component-styles/src/app/quest-summary.component.ts"> <code-example path="component-styles/src/app/quest-summary.component.ts">
</code-example> </code-example>

View File

@ -13,6 +13,7 @@ It's used so widely that almost everyone just calls it _DI_.
This page covers what DI is, why it's so useful, This page covers what DI is, why it's so useful,
and [how to use it](guide/dependency-injection#angular-di) in an Angular app.# Contents and [how to use it](guide/dependency-injection#angular-di) in an Angular app.# Contents
- [Why dependency injection?](guide/dependency-injection#why-di) - [Why dependency injection?](guide/dependency-injection#why-di)
- [Angular dependency injection](guide/dependency-injection#angular-dependency-injection) - [Angular dependency injection](guide/dependency-injection#angular-dependency-injection)
- [Configuring the injector](guide/dependency-injection#injector-config) - [Configuring the injector](guide/dependency-injection#injector-config)
@ -48,7 +49,6 @@ To understand why dependency injection is so important, consider an example with
Imagine writing the following code: Imagine writing the following code:
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car"> <code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car">
</code-example> </code-example>
@ -63,7 +63,7 @@ the very specific classes `Engine` and `Tires`.
What if the `Engine` class evolves and its constructor requires a parameter? What if the `Engine` class evolves and its constructor requires a parameter?
That would break the `Car` class and it would stay broken until you rewrote it along the lines of That would break the `Car` class and it would stay broken until you rewrote it along the lines of
`#{_thisDot}engine = new Engine(theNewParameter)`. `this.engine = new Engine(theNewParameter)`.
The `Engine` constructor parameters weren't even a consideration when you first wrote `Car`. The `Engine` constructor parameters weren't even a consideration when you first wrote `Car`.
You may not anticipate them even now. You may not anticipate them even now.
But you'll *have* to start caring because But you'll *have* to start caring because
@ -99,21 +99,16 @@ How can you make `Car` more robust, flexible, and testable?
That's super easy. Change the `Car` constructor to a version with DI: That's super easy. Change the `Car` constructor to a version with DI:
<code-tabs> <code-tabs>
<code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor"> <code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor">
</code-pane> </code-pane>
<code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor"> <code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
See what happened? The definition of the dependencies are See what happened? The definition of the dependencies are
@ -122,7 +117,6 @@ The `Car` class no longer creates an `engine` or `tires`.
It just consumes them. It just consumes them.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
This example leverages TypeScript's constructor syntax for declaring This example leverages TypeScript's constructor syntax for declaring
@ -134,7 +128,6 @@ parameters and properties simultaneously.
Now you can create a car by passing the engine and tires to the constructor. Now you can create a car by passing the engine and tires to the constructor.
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false">
</code-example> </code-example>
@ -154,7 +147,6 @@ The _consumer_ of `Car` has the problem. The consumer must update the car creati
something like this: something like this:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
</code-example> </code-example>
@ -171,7 +163,6 @@ You can pass mocks to the constructor that do exactly what you want them to do
during each test: during each test:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false"> <code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
</code-example> </code-example>
@ -190,7 +181,6 @@ You need something that takes care of assembling these parts.
You _could_ write a giant class to do that: You _could_ write a giant class to do that:
<code-example path="dependency-injection/src/app/car/car-factory.ts"> <code-example path="dependency-injection/src/app/car/car-factory.ts">
</code-example> </code-example>
@ -210,7 +200,6 @@ You register some classes with this injector, and it figures out how to create t
When you need a `Car`, you simply ask the injector to get it for you and you're good to go. When you need a `Car`, you simply ask the injector to get it for you and you're good to go.
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false"> <code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false">
</code-example> </code-example>
@ -235,33 +224,24 @@ start with a simplified version of the `HeroesComponent`
that from the [The Tour of Heroes](tutorial/). that from the [The Tour of Heroes](tutorial/).
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="v1"> <code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="v1">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.1.ts"> <code-pane title="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero.ts" path="dependency-injection/src/app/heroes/hero.ts"> <code-pane title="src/app/heroes/hero.ts" path="dependency-injection/src/app/heroes/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/mock-heroes.ts" path="dependency-injection/src/app/heroes/mock-heroes.ts"> <code-pane title="src/app/heroes/mock-heroes.ts" path="dependency-injection/src/app/heroes/mock-heroes.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
The `HeroesComponent` is the root component of the *Heroes* feature area. The `HeroesComponent` is the root component of the *Heroes* feature area.
@ -284,13 +264,14 @@ Given that the service is a
[separate concern](https://en.wikipedia.org/wiki/Separation_of_concerns), [separate concern](https://en.wikipedia.org/wiki/Separation_of_concerns),
consider writing the service code in its own file. consider writing the service code in its own file.
See [this note](guide/dependency-injection#one-class-per-file) for details.
~~~ ~~~
The following `HeroService` exposes a `getHeroes` method that returns The following `HeroService` exposes a `getHeroes` method that returns
the same mock data as before, but none of its consumers need to know that. the same mock data as before, but none of its consumers need to know that.
<code-example path="dependency-injection/src/app/heroes/hero.service.1.ts"> <code-example path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-example> </code-example>
@ -299,7 +280,7 @@ the same mock data as before, but none of its consumers need to know that.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The `@Injectable()` #{_decorator} above the service class is The `@Injectable()` decorator above the service class is
covered [shortly](guide/dependency-injection#injectable). covered [shortly](guide/dependency-injection#injectable).
@ -311,7 +292,7 @@ covered [shortly](guide/dependency-injection#injectable).
Of course, this isn't a real service. Of course, this isn't a real service.
If the app were actually getting data from a remote server, the API would have to be If the app were actually getting data from a remote server, the API would have to be
asynchronous, #{_perhaps} returning a !{_PromiseLinked}. asynchronous, perhaps returning a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
You'd also have to rewrite the way components consume the service. You'd also have to rewrite the way components consume the service.
This is important in general, but not in this example. This is important in general, but not in this example.
@ -335,7 +316,6 @@ You don't have to create an Angular injector.
Angular creates an application-wide injector for you during the bootstrap process. Angular creates an application-wide injector for you during the bootstrap process.
<code-example path="dependency-injection/src/main.ts" linenums="false" title="src/main.ts (bootstrap)" region="bootstrap"> <code-example path="dependency-injection/src/main.ts" linenums="false" title="src/main.ts (bootstrap)" region="bootstrap">
</code-example> </code-example>
@ -343,18 +323,16 @@ Angular creates an application-wide injector for you during the bootstrap proces
You do have to configure the injector by registering the **providers** You do have to configure the injector by registering the **providers**
that create the services the application requires. that create the services the application requires.
This guide explains what [providers](guide/dependency-injection#providers) are later. This guide explains what [providers](guide/dependency-injection#providers) are later.
You can either register a provider within an [NgModule](guide/ngmodule) or in application components. You can either register a provider within an [NgModule](guide/ngmodule) or in application components.
{@a register-providers-ngmodule} {@a register-providers-ngmodule}
### Registering providers in an _NgModule_ ### Registering providers in an _NgModule_
Here's the `AppModule` that registers two providers, `UserService` and an `APP_CONFIG` provider, Here's the `AppModule` that registers two providers, `UserService` and an `APP_CONFIG` provider,
in its `providers` !{_array}. in its `providers` array.
<code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)" region="ngmodule">
<code-example path="dependency-injection/app_module_ts + ' (excerpt)'" linenums="false" title="app_module_ts + ' (excerpt)' (ngmodule)" region="ngmodule">
</code-example> </code-example>
@ -366,8 +344,7 @@ place to register it.
{@a register-providers-component} {@a register-providers-component}
### Registering providers in a component ### Registering providers in a component
Here's a revised `HeroesComponent` that registers the `HeroService` in its `providers` !{_array}. Here's a revised `HeroesComponent` that registers the `HeroService` in its `providers` array.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" linenums="false"> <code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" linenums="false">
@ -376,7 +353,6 @@ Here's a revised `HeroesComponent` that registers the `HeroService` in its `prov
{@a ngmodule-vs-comp} {@a ngmodule-vs-comp}
### When to use _NgModule_ versus an application component ### When to use _NgModule_ versus an application component
@ -387,7 +363,7 @@ On the other hand, a provider registered in an application component is availabl
that component and all its children. that component and all its children.
Here, the `APP_CONFIG` service needs to be available all across the application, so it's Here, the `APP_CONFIG` service needs to be available all across the application, so it's
registered in the `AppModule` `@NgModule` `providers` !{_array}. registered in the `AppModule` `@NgModule` `providers` array.
But since the `HeroService` is only used within the *Heroes* But since the `HeroService` is only used within the *Heroes*
feature area and nowhere else, it makes sense to register it in feature area and nowhere else, it makes sense to register it in
the `HeroesComponent`. the `HeroesComponent`.
@ -412,21 +388,16 @@ constructor, [as discussed earlier](guide/dependency-injection#ctor-injection).
It's a small change: It's a small change:
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/hero-list.component (with DI)" path="dependency-injection/src/app/heroes/hero-list.component.2.ts"> <code-pane title="src/app/heroes/hero-list.component (with DI)" path="dependency-injection/src/app/heroes/hero-list.component.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero-list.component (without DI)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts"> <code-pane title="src/app/heroes/hero-list.component (without DI)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -438,18 +409,17 @@ It's a small change:
Adding a parameter to the constructor isn't all that's happening here. Adding a parameter to the constructor isn't all that's happening here.
<code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" linenums="false">
</code-example> </code-example>
Note that the constructor parameter has the type `HeroService`, and that Note that the constructor parameter has the type `HeroService`, and that
the `HeroListComponent` class has an `@Component` #{_decorator} the `HeroListComponent` class has an `@Component` decorator
(scroll up to confirm that fact). (scroll up to confirm that fact).
Also recall that the parent component (`HeroesComponent`) Also recall that the parent component (`HeroesComponent`)
has `providers` information for `HeroService`. has `providers` information for `HeroService`.
The constructor parameter type, the `@Component` #{_decorator}, The constructor parameter type, the `@Component` decorator,
and the parent's `providers` information combine to tell the and the parent's `providers` information combine to tell the
Angular injector to inject an instance of Angular injector to inject an instance of
`HeroService` whenever it creates a new `HeroListComponent`. `HeroService` whenever it creates a new `HeroListComponent`.
@ -468,7 +438,6 @@ You _could_ create such an injector
explicitly: explicitly:
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false"> <code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false">
</code-example> </code-example>
@ -505,7 +474,6 @@ For example, you can create a new `HeroListComponent` with a mock service that y
under test: under test:
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false"> <code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false">
</code-example> </code-example>
@ -534,37 +502,31 @@ adding a constructor that takes a `Logger` parameter.
Here is the revision compared to the original. Here is the revision compared to the original.
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts"> <code-pane title="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts"> <code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `#{_priv}logger`. The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `logger`.
You call that property within the `getHeroes()` method when anyone asks for heroes. You call that property within the `getHeroes()` method when anyone asks for heroes.
{@a injectable} {@a injectable}
### Why _@Injectable()_? ### Why _@Injectable()_?
**<a href="#{injUrl}">@Injectable()</a>** marks a class as available to an **<a href="../api/core/index/Injectable-decorator.html">@Injectable()</a>** marks a class as available to an
injector for instantiation. Generally speaking, an injector reports an injector for instantiation. Generally speaking, an injector reports an
error when trying to instantiate a class that is not marked as error when trying to instantiate a class that is not marked as
`@Injectable()`. `@Injectable()`.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
As it happens, you could have omitted `@Injectable()` from the first As it happens, you could have omitted `@Injectable()` from the first
@ -592,18 +554,14 @@ and, therefore, do not technically require it. Here's why:
<ul style="font-size:inherit"> <ul style="font-size:inherit">
<li> <li>
<b>Future proofing:</b> No need to remember <code>@Injectable()</code> when you add a dependency later. <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when you add a dependency later.
</li> </li>
<li> <li>
<b>Consistency:</b> All services follow the same rules, and you don't have to wonder why is missing. <b>Consistency:</b> All services follow the same rules, and you don't have to wonder why a decorator is missing.
</li> </li>
</ul> </ul>
@ -616,12 +574,34 @@ like `HeroesComponent`. So why doesn't `HeroesComponent` have
You *can* add it if you really want to. It isn't necessary because the You *can* add it if you really want to. It isn't necessary because the
`HeroesComponent` is already marked with `@Component`, and this `HeroesComponent` is already marked with `@Component`, and this
!{_decorator} class (like `@Directive` and `@Pipe`, which you learn about later) decorator class (like `@Directive` and `@Pipe`, which you learn about later)
is a subtype of <a href="#{injUrl}">@Injectable()</a>. It is in is a subtype of <a href="../api/core/index/Injectable-decorator.html">@Injectable()</a>. It is in
fact `@Injectable()` #{_decorator}s that fact `@Injectable()` decorators that
identify a class as a target for instantiation by an injector. identify a class as a target for instantiation by an injector.
~~~ {.l-sub-section}
At runtime, injectors can read class metadata in the transpiled JavaScript code
and use the constructor parameter type information
to determine what things to inject.
Not every JavaScript class has metadata.
The TypeScript compiler discards metadata by default.
If the `emitDecoratorMetadata` compiler option is true
(as it should be in the `tsconfig.json`),
the compiler adds the metadata to the generated JavaScript
for _every class with at least one decorator_.
While any decorator will trigger this effect, mark the service class with the
<a href="../api/core/index/Injectable-decorator.html">@Injectable()</a> decorator
to make the intent clear.
~~~
~~~ {.callout.is-critical} ~~~ {.callout.is-critical}
@ -630,7 +610,6 @@ identify a class as a target for instantiation by an injector.
Always include the parentheses Always include the parentheses
</header> </header>
Always write `@Injectable()`, not just `@Injectable`. Always write `@Injectable()`, not just `@Injectable`.
The application will fail mysteriously if you forget the parentheses. The application will fail mysteriously if you forget the parentheses.
@ -647,16 +626,13 @@ Inject a logger into `HeroService` in two steps:
The logger service is quite simple: The logger service is quite simple:
<code-example path="dependency-injection/src/app/logger.service.ts"> <code-example path="dependency-injection/src/app/logger.service.ts">
</code-example> </code-example>
You're likely to need the same logger service everywhere in your application, You're likely to need the same logger service everywhere in your application,
so put it in the project's `#{_appDir}` folder and so put it in the project's `app` folder and
register it in the `providers` #{_array} of the application !{_moduleVsComp}, `!{_AppModuleVsAppComp}`. register it in the `providers` array of the application module, `AppModule`.
<code-example path="dependency-injection/src/app/providers.component.ts" linenums="false" title="src/app/providers.component.ts (excerpt)" region="providers-logger"> <code-example path="dependency-injection/src/app/providers.component.ts" linenums="false" title="src/app/providers.component.ts (excerpt)" region="providers-logger">
@ -685,20 +661,19 @@ that the injector injects into components and other services.
You must register a service *provider* with the injector, or it won't know how to create the service. You must register a service *provider* with the injector, or it won't know how to create the service.
Earlier you registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppModule` like this: Earlier you registered the `Logger` service in the `providers` array of the metadata for the `AppModule` like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example> </code-example>
There are many ways to *provide* something that #{implements} `Logger`. There are many ways to *provide* something that looks and behaves like a `Logger`.
The `Logger` class itself is an obvious and natural provider. The `Logger` class itself is an obvious and natural provider.
But it's not the only way. But it's not the only way.
You can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`. You can configure the injector with alternative providers that can deliver an object that behaves like a `Logger`.
You could provide a substitute class. #{loggerlike} You could provide a substitute class. You could provide a logger-like object.
You could give it a provider that calls a logger factory function. You could give it a provider that calls a logger factory function.
Any of these approaches might be a good choice under the right circumstances. Any of these approaches might be a good choice under the right circumstances.
@ -709,30 +684,26 @@ What matters is that the injector has a provider to go to when it needs a `Logge
</div> </div>
### The *Provider* class !{_andProvideFn} ### The *Provider* class and _provide_ object literal
You wrote the `providers` #{_array} like this: You wrote the `providers` array like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1">
</code-example> </code-example>
This is actually a shorthand expression for a provider registration This is actually a shorthand expression for a provider registration
using a _provider_ object literal with two properties: using a _provider_ object literal with two properties:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3">
</code-example> </code-example>
The first is the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value The first is the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value
and registering the provider. and registering the provider.
The second is a !{_secondParam}, The second is a provider definition object,
which you can think of as a *recipe* for creating the dependency value. which you can think of as a *recipe* for creating the dependency value.
There are many ways to create dependency values just as there are many ways to write a recipe. There are many ways to create dependency values just as there are many ways to write a recipe.
@ -748,14 +719,12 @@ The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`. to return a `BetterLogger` when something asks for the `Logger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4">
</code-example> </code-example>
{@a class-provider-dependencies} {@a class-provider-dependencies}
### Class provider with dependencies ### Class provider with dependencies
Maybe an `EvenBetterLogger` could display the user name in the log message. Maybe an `EvenBetterLogger` could display the user name in the log message.
@ -763,7 +732,6 @@ This logger gets the user from the injected `UserService`,
which is also injected at the application level. which is also injected at the application level.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example> </code-example>
@ -771,7 +739,6 @@ which is also injected at the application level.
Configure it like `BetterLogger`. Configure it like `BetterLogger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example> </code-example>
@ -796,7 +763,6 @@ You certainly do not want two different `NewLogger` instances in your app.
Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`. Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example> </code-example>
@ -804,7 +770,6 @@ Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger
The solution: alias with the `useExisting` option. The solution: alias with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example> </code-example>
@ -816,8 +781,6 @@ The solution: alias with the `useExisting` option.
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example> </code-example>
@ -826,7 +789,6 @@ Then you register a provider with the `useValue` option,
which makes this object play the logger role. which makes this object play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example> </code-example>
@ -865,7 +827,6 @@ who is authorized and who is not.
Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes. Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false">
</code-example> </code-example>
@ -876,7 +837,6 @@ You'll have to take over the creation of new instances of this `HeroService` wit
A factory provider needs a factory function: A factory provider needs a factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false">
</code-example> </code-example>
@ -887,7 +847,6 @@ You inject both the `Logger` and the `UserService` into the factory provider
and let the injector pass them along to the factory function: and let the injector pass them along to the factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false"> <code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false">
</code-example> </code-example>
@ -899,37 +858,32 @@ and let the injector pass them along to the factory function:
The `useFactory` field tells Angular that the provider is a factory function The `useFactory` field tells Angular that the provider is a factory function
whose implementation is the `heroServiceFactory`. whose implementation is the `heroServiceFactory`.
The `deps` property is #{_an} #{_array} of [provider tokens](guide/dependency-injection#token). The `deps` property is an array of [provider tokens](guide/dependency-injection#token).
The `Logger` and `UserService` classes serve as tokens for their own class providers. The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
~~~ ~~~
Notice that you captured the factory provider in #{_an} #{exportedvar}, `heroServiceProvider`. Notice that you captured the factory provider in an exported variable, `heroServiceProvider`.
This extra step makes the factory provider reusable. This extra step makes the factory provider reusable.
You can register the `HeroService` with this #{variable} wherever you need it. You can register the `HeroService` with this variable wherever you need it.
In this sample, you need it only in the `HeroesComponent`, In this sample, you need it only in the `HeroesComponent`,
where it replaces the previous `HeroService` registration in the metadata `providers` #{_array}. where it replaces the previous `HeroService` registration in the metadata `providers` array.
Here you see the new and the old implementation side-by-side: Here you see the new and the old implementation side-by-side:
<code-tabs> <code-tabs>
<code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts"> <code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full"> <code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -944,7 +898,6 @@ the class *type* served as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false"> <code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false">
</code-example> </code-example>
@ -955,7 +908,6 @@ Angular knows to inject the
service associated with that `HeroService` class token: service associated with that `HeroService` class token:
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature"> <code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature">
</code-example> </code-example>
@ -982,14 +934,12 @@ This is especially convenient when you consider that most dependency values are
<code-example path="dependency-injection/src/app/app.config.ts" region="config" linenums="false"> <code-example path="dependency-injection/src/app/app.config.ts" region="config" linenums="false">
</code-example> </code-example>
What if you'd like to make this configuration object available for injection? What if you'd like to make this configuration object available for injection?
You know you can register an object with a [value provider](guide/dependency-injection#value-provider). You know you can register an object with a [value provider](guide/dependency-injection#value-provider).
But what should you use as the token? But what should you use as the token?
You don't have a class to serve as a token. You don't have a class to serve as a token.
There is no `AppConfig` class. There is no `AppConfig` class.
@ -1002,14 +952,12 @@ There is no `AppConfig` class.
The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you
cannot use a TypeScript interface as a token: cannot use a TypeScript interface as a token:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example> </code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example> </code-example>
@ -1030,11 +978,10 @@ There is no interface type information left for Angular to find at runtime.
### _OpaqueToken_ ### _OpaqueToken_
One solution to choosing a provider token for non-class dependencies is One solution to choosing a provider token for non-class dependencies is
to define and use an !{opaquetoken}. to define and use an <a href="../api/core/index/OpaqueToken-class.html"><b>OpaqueToken</b></a>.
The definition looks like this: The definition looks like this:
<code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false"> <code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false">
</code-example> </code-example>
@ -1042,14 +989,12 @@ The definition looks like this:
Register the dependency provider using the `OpaqueToken` object: Register the dependency provider using the `OpaqueToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
</code-example> </code-example>
Now you can inject the configuration object into any constructor that needs it, with Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject` #{_decorator}: the help of an `@Inject` decorator:
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false"> <code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false">
@ -1060,21 +1005,15 @@ the help of an `@Inject` #{_decorator}:
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Although the !{configType} interface plays no role in dependency injection, Although the `AppConfig` interface plays no role in dependency injection,
it supports typing of the configuration object within the class. it supports typing of the configuration object within the class.
~~~ ~~~
Aternatively, you can provide and inject the configuration object in an ngModule like `AppModule`. Aternatively, you can provide and inject the configuration object in an ngModule like `AppModule`.
+makeExcerpt('src/app/app.module.ts','ngmodule-providers')
<code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (ngmodule-providers)" region="ngmodule-providers">
</code-example>
<div id='optional'> <div id='optional'>
@ -1089,6 +1028,11 @@ You can tell Angular that the dependency is optional by annotating the
constructor argument with `@Optional()`: constructor argument with `@Optional()`:
<code-example path="dependency-injection/src/app/providers.component.ts" region="import-optional">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false"> <code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
@ -1116,7 +1060,6 @@ Developers rarely work directly with an injector, but
here's an `InjectorComponent` that does. here's an `InjectorComponent` that does.
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector"> <code-example path="dependency-injection/src/app/injector.component.ts" region="injector">
</code-example> </code-example>
@ -1152,3 +1095,25 @@ must acquire services generically and dynamically.
~~~ ~~~
## Appendix: Why have one class per file
Having multiple classes in the same file is confusing and best avoided.
Developers expect one class per file. Keep them happy.
If you combine the `HeroService` class with
the `HeroesComponent` in the same file,
**define the component last**.
If you define the component before the service,
you'll get a runtime null reference error.
~~~ {.l-sub-section}
You actually can define the component first with the help of the `forwardRef()` method as explained
in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
But why flirt with trouble?
Avoid the problem altogether by defining components and services in separate files.
~~~

View File

@ -10,6 +10,7 @@ This page describes tools and techniques for deploy and optimize your Angular ap
{@a toc} {@a toc}
## Table of contents ## Table of contents
* [Overview](guide/deployment#overview) * [Overview](guide/deployment#overview)
* [Simplest deployment possible](guide/deployment#dev-deploy) * [Simplest deployment possible](guide/deployment#dev-deploy)
* [Optimize for production](guide/deployment#optimize) * [Optimize for production](guide/deployment#optimize)
@ -108,7 +109,6 @@ Load the few files you need from the web instead.
with versions that load from the web. It might look like this. with versions that load from the web. It might look like this.
<code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false"> <code-example path="deployment/src/index.html" region="node-module-scripts" linenums="false">
</code-example> </code-example>
@ -116,7 +116,6 @@ with versions that load from the web. It might look like this.
(2) Replace the `systemjs.config.js` script with a script that (2) Replace the `systemjs.config.js` script with a script that
loads `systemjs.config.server.js`. loads `systemjs.config.server.js`.
<code-example path="deployment/src/index.html" region="systemjs-config" linenums="false"> <code-example path="deployment/src/index.html" region="systemjs-config" linenums="false">
</code-example> </code-example>
@ -131,7 +130,6 @@ you make to `systemjs.config.js`.
Notice the `paths` key: Notice the `paths` key:
<code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false"> <code-example path="deployment/src/systemjs.config.server.js" region="paths" linenums="false">
</code-example> </code-example>
@ -153,57 +151,42 @@ Then change the config's `'npm'` path to point to that folder.
The following trivial router sample app shows these changes. The following trivial router sample app shows these changes.
<code-tabs> <code-tabs>
<code-pane title="index.html" path="deployment/src/index.html"> <code-pane title="index.html" path="deployment/src/index.html">
</code-pane> </code-pane>
<code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js"> <code-pane title="systemjs.config.server.js" path="deployment/src/systemjs.config.server.js">
</code-pane> </code-pane>
<code-pane title="main.ts" path="deployment/src/main.ts"> <code-pane title="main.ts" path="deployment/src/main.ts">
</code-pane> </code-pane>
<code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts"> <code-pane title="app/app.module.ts" path="deployment/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts"> <code-pane title="app/app.component.ts" path="deployment/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts"> <code-pane title="app/crisis-list.component.ts" path="deployment/src/app/crisis-list.component.ts">
</code-pane> </code-pane>
<code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts"> <code-pane title="app/hero-list.component.ts" path="deployment/src/app/hero-list.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Practice with this sample before attempting these techniques on your application. Practice with this sample before attempting these techniques on your application.
1. Follow the [setup instructions](guide/setup) for creating a new project 1. Follow the [setup instructions](guide/setup) for creating a new project
named <ngio-ex path="simple-deployment"></ngio-ex>. named <code>simple-deployment</code>.
1. Add the "Simple deployment" sample files shown above. 1. Add the "Simple deployment" sample files shown above.
@ -390,7 +373,6 @@ Switching to production mode can make it run faster by disabling development spe
To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`. To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`.
<code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false"> <code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false">
</code-example> </code-example>

View File

@ -22,7 +22,7 @@ The final UI looks like this:
# Contents # Contents
* [Showing component properties with interpolation](guide/displaying-data#interpolation). * [Showing component properties with interpolation](guide/displaying-data#interpolation).
* [Showing !{_an} !{_array} property with NgFor](guide/displaying-data#ngFor). * [Showing an array property with NgFor](guide/displaying-data#ngFor).
* [Conditional display with NgIf](guide/displaying-data#ngIf). * [Conditional display with NgIf](guide/displaying-data#ngIf).
@ -41,15 +41,14 @@ is to bind the property name through interpolation.
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`. With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
Follow the [setup](guide/setup) instructions for creating a new project Follow the [setup](guide/setup) instructions for creating a new project
named <ngio-ex path="displaying-data"></ngio-ex>. named <code>displaying-data</code>.
Then modify the <ngio-ex path="app.component.ts"></ngio-ex> file by Then modify the <code>app.component.ts</code> file by
changing the template and the body of the component. changing the template and the body of the component.
When you're done, it should look like this: When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts"> <code-example path="displaying-data/src/app/app.component.1.ts">
</code-example> </code-example>
@ -60,11 +59,22 @@ The revised template displays the two component properties using double curly br
interpolation: interpolation:
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
~~~ {.l-sub-section}
The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>).
The backtick (<code>\`</code>)&mdash;which is *not* the same character as a single
quote (`'`)&mdash;allows you to compose a string over several lines, which makes the
HTML more readable.
~~~
Angular automatically pulls the value of the `title` and `myHero` properties from the component and Angular automatically pulls the value of the `title` and `myHero` properties from the component and
inserts those values into the browser. Angular updates the display inserts those values into the browser. Angular updates the display
when these properties change. when these properties change.
@ -81,16 +91,15 @@ the view, such as a keystroke, a timer completion, or a response to an HTTP requ
Notice that you don't call **new** to create an instance of the `AppComponent` class. Notice that you don't call **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for you. How? Angular is creating an instance for you. How?
The CSS `selector` in the `@Component` !{_decorator} specifies an element named `<my-app>`. The CSS `selector` in the `@Component` decorator specifies an element named `<my-app>`.
That element is a placeholder in the body of your `index.html` file: That element is a placeholder in the body of your `index.html` file:
<code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body"> <code-example path="displaying-data/src/index.html" linenums="false" title="src/index.html (body)" region="body">
</code-example> </code-example>
When you bootstrap with the `AppComponent` class (in <ngio-ex path="main.ts"></ngio-ex>), Angular looks for a `<my-app>` When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<my-app>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag. inside the `<my-app>` tag.
@ -100,12 +109,13 @@ Now run the app. It should display the title and hero name:
<img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img> <img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img>
</figure> </figure>
The next few sections review some of the coding choices in the app.
## Template inline or template file? ## Template inline or template file?
You can store your component's template in one of two places. You can store your component's template in one of two places.
You can define it *inline* using the `template` property, or you can define You can define it *inline* using the `template` property, or you can define
the template in a separate HTML file and link to it in the template in a separate HTML file and link to it in
the component metadata using the `@Component` !{_decorator}'s `templateUrl` property. the component metadata using the `@Component` decorator's `templateUrl` property.
The choice between inline and separate HTML is a matter of taste, The choice between inline and separate HTML is a matter of taste,
circumstances, and organization policy. circumstances, and organization policy.
@ -113,11 +123,20 @@ Here the app uses inline HTML because the template is small and the demo
is simpler without the additional HTML file. is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties. In either style, the template data bindings have the same access to the component's properties.
## Constructor or variable initialization?
## Showing !{_an} !{_array} property with ***ngFor** Although this example uses variable assignment to initialize the components, you can instead declare and initialize the properties using a constructor:
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-ctor.component.ts" linenums="false" title="src/app/app-ctor.component.ts (class)" region="class">
</code-example>
This app uses more terse "variable assignment" style simply for brevity.
## Showing an array property with ***ngFor**
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (class)" region="class"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (class)" region="class">
@ -128,7 +147,6 @@ Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list. each item in the `heroes` list.
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
@ -138,7 +156,6 @@ in the `<li>` element is the Angular "repeater" directive.
It marks that `<li>` element (and its children) as the "repeater template": It marks that `<li>` element (and its children) as the "repeater template":
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (li)" region="li"> <code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" title="src/app/app.component.ts (li)" region="li">
</code-example> </code-example>
@ -165,8 +182,8 @@ context for the interpolation in the double curly braces.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
In this case, `ngFor` is displaying !{_an} !{_array}, but `ngFor` can In this case, `ngFor` is displaying an array, but `ngFor` can
repeat items for any [iterable](guide/!{_iterableUrl}) object. repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) object.
~~~ ~~~
@ -183,21 +200,19 @@ Now the heroes appear in an unordered list.
The app's code defines the data directly inside the component, which isn't best practice. The app's code defines the data directly inside the component, which isn't best practice.
In a simple demo, however, it's fine. In a simple demo, however, it's fine.
At the moment, the binding is to !{_an} !{_array} of strings. At the moment, the binding is to an array of strings.
In real applications, most bindings are to more specialized objects. In real applications, most bindings are to more specialized objects.
To convert this binding to use specialized objects, turn the !{_array} To convert this binding to use specialized objects, turn the array
of hero names into !{_an} !{_array} of `Hero` objects. For that you'll need a `Hero` class. of hero names into an array of `Hero` objects. For that you'll need a `Hero` class.
Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code:
Create a new file in the `app` folder called `hero.ts` with the following code:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)"> <code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)">
</code-example> </code-example>
You've defined a class with a constructor and two properties: `id` and `name`. You've defined a class with a constructor and two properties: `id` and `name`.
It might not look like the class has properties, but it does. It might not look like the class has properties, but it does.
@ -206,7 +221,6 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
Consider the first parameter: Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id"> <code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (id)" region="id">
</code-example> </code-example>
@ -218,11 +232,10 @@ That brief syntax does a lot:
## Using the Hero class ## Using the Hero class
After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ !{_array} After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ array
of `Hero` objects: of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes"> <code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (heroes)" region="heroes">
</code-example> </code-example>
@ -232,7 +245,6 @@ At the moment it displays the hero's `id` and `name`.
Fix that to display only the hero's `name` property. Fix that to display only the hero's `name` property.
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (template)" region="template"> <code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" title="src/app/app.component.ts (template)" region="template">
</code-example> </code-example>
@ -245,11 +257,10 @@ Sometimes an app needs to display a view or a portion of a view only under speci
Let's change the example to display a message if there are more than three heroes. Let's change the example to display a message if there are more than three heroes.
The Angular `ngIf` directive inserts or removes an element based on a !{_boolean} condition. The Angular `ngIf` directive inserts or removes an element based on a _truthy/falsy_ condition.
To see it in action, add the following paragraph at the bottom of the template: To see it in action, add the following paragraph at the bottom of the template:
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (message)" region="message"> <code-example path="displaying-data/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (message)" region="message">
</code-example> </code-example>
@ -265,7 +276,7 @@ Read more about `ngIf` and `*` in the [ngIf section](guide/template-syntax) of t
~~~ ~~~
The template expression inside the double quotes, The template expression inside the double quotes,
`*ngIf="heros.length > 3"`, looks and behaves much like !{_Lang}. `*ngIf="heros.length > 3"`, looks and behaves much like TypeScript.
When the component's list of heroes has more than three items, Angular adds the paragraph When the component's list of heroes has more than three items, Angular adds the paragraph
to the DOM and the message appears. If there are three or fewer items, Angular omits the to the DOM and the message appears. If there are three or fewer items, Angular omits the
paragraph, so no message appears. For more information, paragraph, so no message appears. For more information,
@ -281,47 +292,37 @@ big chunks of HTML with many data bindings.
~~~ ~~~
Try it out. Because the !{_array} has four items, the message should appear. Try it out. Because the array has four items, the message should appear.
Go back into <ngio-ex path="app.component.ts"></ngio-ex> and delete or comment out one of the elements from the hero !{_array}. Go back into <code>app.component.ts"</code> and delete or comment out one of the elements from the hero array.
The browser should refresh automatically and the message should disappear. The browser should refresh automatically and the message should disappear.
## Summary ## Summary
Now you know how to use: Now you know how to use:
- **Interpolation** with double curly braces to display a component property. - **Interpolation** with double curly braces to display a component property.
- **ngFor** to display !{_an} !{_array} of items. - **ngFor** to display an array of items.
- A !{_Lang} class to shape the **model data** for your component and display properties of that model. - A TypeScript class to shape the **model data** for your component and display properties of that model.
- **ngIf** to conditionally display a chunk of HTML based on a boolean expression. - **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
Here's the final code: Here's the final code:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final"> <code-pane title="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
</code-pane> </code-pane>
<code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts"> <code-pane title="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="displaying-data/src/main.ts"> <code-pane title="main.ts" path="displaying-data/src/main.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

View File

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

View File

@ -43,21 +43,16 @@ Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, s
We bootstrap our `AppModule` in main.ts. We bootstrap our `AppModule` in main.ts.
<code-tabs> <code-tabs>
<code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts"> <code-pane title="app.module.ts" path="cb-dynamic-form/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="cb-dynamic-form/src/main.ts"> <code-pane title="main.ts" path="cb-dynamic-form/src/main.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -70,7 +65,6 @@ The "question" is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class. We have created `QuestionBase` as the most fundamental question class.
<code-example path="cb-dynamic-form/src/app/question-base.ts"> <code-example path="cb-dynamic-form/src/app/question-base.ts">
</code-example> </code-example>
@ -81,7 +75,6 @@ The idea is that the form will be bound to specific question types and render th
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property. `TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property.
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false">
</code-example> </code-example>
@ -89,7 +82,6 @@ The idea is that the form will be bound to specific question types and render th
`DropdownQuestion` presents a list of choices in a select box. `DropdownQuestion` presents a list of choices in a select box.
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false">
</code-example> </code-example>
@ -98,7 +90,6 @@ Next we have defined `QuestionControlService`, a simple service for transforming
In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules. In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules.
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false"> <code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false">
</code-example> </code-example>
@ -107,21 +98,16 @@ In a nutshell, the form group consumes the metadata from the question model and
Now that we have defined the complete model we are ready to create components to represent the dynamic form. Now that we have defined the complete model we are ready to create components to represent the dynamic form.
`DynamicFormComponent` is the entry point and the main container for the form. `DynamicFormComponent` is the entry point and the main container for the form.
<code-tabs> <code-tabs>
<code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html"> <code-pane title="dynamic-form.component.html" path="cb-dynamic-form/src/app/dynamic-form.component.html">
</code-pane> </code-pane>
<code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts"> <code-pane title="dynamic-form.component.ts" path="cb-dynamic-form/src/app/dynamic-form.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
It presents a list of questions, each question bound to a `<df-question>` component element. It presents a list of questions, each question bound to a `<df-question>` component element.
@ -129,21 +115,16 @@ The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object. the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object.
<code-tabs> <code-tabs>
<code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html"> <code-pane title="dynamic-form-question.component.html" path="cb-dynamic-form/src/app/dynamic-form-question.component.html">
</code-pane> </code-pane>
<code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts"> <code-pane title="dynamic-form-question.component.ts" path="cb-dynamic-form/src/app/dynamic-form-question.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Notice this component can present any type of question in our model. Notice this component can present any type of question in our model.
@ -163,7 +144,6 @@ underlying control objects, populated from the question model with display and v
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array. Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array.
<code-example path="cb-dynamic-form/src/app/question.service.ts"> <code-example path="cb-dynamic-form/src/app/question.service.ts">
</code-example> </code-example>
@ -171,7 +151,6 @@ underlying control objects, populated from the question model with display and v
Finally, we display an instance of the form in the `AppComponent` shell. Finally, we display an instance of the form in the `AppComponent` shell.
<code-example path="cb-dynamic-form/src/app/app.component.ts"> <code-example path="cb-dynamic-form/src/app/app.component.ts">
</code-example> </code-example>

View File

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

View File

@ -91,7 +91,7 @@ You'll build this form in small steps:
## Setup ## Setup
Follow the [setup](guide/setup) instructions for creating a new project Follow the [setup](guide/setup) instructions for creating a new project
named <span ngio-ex>angular-forms</span>. named angular-forms.
## Create the Hero model class ## Create the Hero model class
@ -102,8 +102,7 @@ A model can be as simple as a "property bag" that holds facts about a thing of a
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`) That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
and one optional field (`alterEgo`). and one optional field (`alterEgo`).
In the `!{_appDir}` directory, create the following file with the given content: In the `app` directory, create the following file with the given content:
<code-example path="forms/src/app/hero.ts"> <code-example path="forms/src/app/hero.ts">
@ -120,7 +119,6 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
You can create a new hero like this: You can create a new hero like this:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
</code-example> </code-example>
@ -135,7 +133,6 @@ Begin with the class because it states, in brief, what the hero editor can do.
Create the following file with the given content: Create the following file with the given content:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1">
</code-example> </code-example>
@ -180,7 +177,6 @@ Because template-driven forms are in their own module, you need to add the `Form
Replace the contents of the "QuickStart" version with the following: Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.module.ts"> <code-example path="forms/src/app/app.module.ts">
</code-example> </code-example>
@ -220,7 +216,6 @@ If you wrote it and it should belong to this module, _do_ declare it in th
Replace the contents of the "QuickStart" version with the following: Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.component.ts"> <code-example path="forms/src/app/app.component.ts">
</code-example> </code-example>
@ -243,7 +238,6 @@ You've also dropped the `name` field from the class body.
Create the template file with the following contents: Create the template file with the following contents:
<code-example path="forms/src/app/hero-form.component.html" region="start"> <code-example path="forms/src/app/hero-form.component.html" region="start">
</code-example> </code-example>
@ -280,7 +274,6 @@ the styles of any external library. Angular apps can use any CSS library or none
To add the stylesheet, open `index.html` and add the following link to the `<head>`: To add the stylesheet, open `index.html` and add the following link to the `<head>`:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap"> <code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
</code-example> </code-example>
@ -298,7 +291,6 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
Add the following HTML *immediately below* the *Alter Ego* group: Add the following HTML *immediately below* the *Alter Ego* group:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers">
</code-example> </code-example>
@ -331,7 +323,6 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this: Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example> </code-example>
@ -396,7 +387,6 @@ Then you can confirm that two-way data binding works *for the entire hero model*
After revision, the core of the form should look like this: After revision, the core of the form should look like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example> </code-example>
@ -435,109 +425,76 @@ You can leverage those class names to change the appearance of the control.
<table> <table>
<tr> <tr>
<th> <th>
State State
</th> </th>
<th> <th>
Class if true Class if true
</th> </th>
<th> <th>
Class if false Class if false
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
The control has been visited. The control has been visited.
</td> </td>
<td> <td>
<code>ng-touched</code> <code>ng-touched</code>
</td> </td>
<td> <td>
<code>ng-untouched</code> <code>ng-untouched</code>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
The control's value has changed. The control's value has changed.
</td> </td>
<td> <td>
<code>ng-dirty</code> <code>ng-dirty</code>
</td> </td>
<td> <td>
<code>ng-pristine</code> <code>ng-pristine</code>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
The control's value is valid. The control's value is valid.
</td> </td>
<td> <td>
<code>ng-valid</code> <code>ng-valid</code>
</td> </td>
<td> <td>
<code>ng-invalid</code> <code>ng-invalid</code>
</td> </td>
</tr> </tr>
</table> </table>
Temporarily add a [template reference variable](guide/template-syntax) named `spy` Temporarily add a [template reference variable](guide/template-syntax) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes. to the _Name_ `<input>` tag and use it to display the input's CSS classes.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2">
</code-example> </code-example>
@ -584,7 +541,6 @@ You achieve this effect by adding these class definitions to a new `forms.css` f
that you add to the project as a sibling to `index.html`: that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/forms.css"> <code-example path="forms/src/forms.css">
</code-example> </code-example>
@ -592,7 +548,6 @@ that you add to the project as a sibling to `index.html`:
Update the `<head>` of `index.html` to include this style sheet: Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles"> <code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
</code-example> </code-example>
@ -617,7 +572,6 @@ To achieve this effect, extend the `<input>` tag with the following:
Here's an example of an error message added to the _name_ input box: Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg">
</code-example> </code-example>
@ -640,7 +594,6 @@ You control visibility of the name error message by binding properties of the `n
control to the message `<div>` element's `hidden` property. control to the message `<div>` element's `hidden` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
</code-example> </code-example>
@ -668,14 +621,12 @@ Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method. Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset"> <code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset">
</code-example> </code-example>
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false"> <code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false">
</code-example> </code-example>
@ -698,7 +649,6 @@ You have to clear all of the flags imperatively, which you can do
by calling the form's `reset()` method after calling the `newHero()` method. by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset"> <code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset">
</code-example> </code-example>
@ -717,7 +667,6 @@ To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method: to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example> </code-example>
@ -751,7 +700,6 @@ the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code: using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button">
</code-example> </code-example>
@ -794,7 +742,6 @@ Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property. its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div">
</code-example> </code-example>
@ -804,7 +751,6 @@ The main form is visible from the start because the
as this fragment from the `HeroFormComponent` shows: as this fragment from the `HeroFormComponent` shows:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted"> <code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted">
</code-example> </code-example>
@ -816,7 +762,6 @@ Now the app needs to show something else while the form is in the submitted stat
Add the following HTML below the `<div>` wrapper you just wrote: Add the following HTML below the `<div>` wrapper you just wrote:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted"> <code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted">
</code-example> </code-example>
@ -849,139 +794,96 @@ The final project folder structure should look like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-forms angular-forms
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-form.component.html hero-form.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-form.component.ts hero-form.component.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
Heres the code for the final version of the application: Heres the code for the final version of the application:
<code-tabs> <code-tabs>
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final"> <code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
</code-pane> </code-pane>
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final"> <code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
</code-pane> </code-pane>
<code-pane title="hero.ts" path="forms/src/app/hero.ts"> <code-pane title="hero.ts" path="forms/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="app.module.ts" path="forms/src/app/app.module.ts"> <code-pane title="app.module.ts" path="forms/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts"> <code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="main.ts" path="forms/src/main.ts"> <code-pane title="main.ts" path="forms/src/main.ts">
</code-pane> </code-pane>
<code-pane title="index.html" path="forms/src/index.html"> <code-pane title="index.html" path="forms/src/index.html">
</code-pane> </code-pane>
<code-pane title="forms.css" path="forms/src/forms.css"> <code-pane title="forms.css" path="forms/src/forms.css">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

View File

@ -20,11 +20,26 @@ unexpected definitions.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
You can compile Angular applications at build time. You can compile Angular applications at build time.
By compiling your application<span if-docs="ts"> using the compiler-cli, `ngc`</span>, you can bootstrap directly By compiling your application using the compiler-cli, `ngc`, you can bootstrap directly
to a<span if-docs="ts"> module</span> factory, meaning you don't need to include the Angular compiler in your JavaScript bundle. to a module factory, meaning you don't need to include the Angular compiler in your JavaScript bundle.
Ahead-of-time compiled applications also benefit from decreased load time and increased performance. Ahead-of-time compiled applications also benefit from decreased load time and increased performance.
~~~
## Angular module
~~~ {.l-sub-section}
Helps you organize an application into cohesive blocks of functionality.
An Angular module identifies the components, directives, and pipes that the application uses along with the list of external Angular modules that the application needs, such as `FormsModule`.
Every Angular application has an application root-module class. By convention, the class is
called `AppModule` and resides in a file named `app.module.ts`.
For details and examples, see the [Angular Modules (NgModule)](guide/ngmodule) page.
~~~ ~~~
## Annotation ## Annotation
@ -52,12 +67,68 @@ as HTML attributes, hence the name.
For example, you can use the `ngClass` directive to add and remove CSS class names. For example, you can use the `ngClass` directive to add and remove CSS class names.
Learn about them in the [_Attribute Directives_](guide/!{docsLatest}/guide/attribute-directives) guide. Learn about them in the [_Attribute Directives_](guide/attribute-directives) guide.
~~~ ~~~
## Barrel
~~~ {.l-sub-section}
A way to *roll up exports* from several ES2015 modules into a single convenient ES2015 module.
The barrel itself is an ES2015 module file that re-exports *selected* exports of other ES2015 modules.
For example, imagine three ES2015 modules in a `heroes` folder:
<code-example>
// heroes/hero.component.ts
export class HeroComponent {}
// heroes/hero.model.ts
export class Hero {}
// heroes/hero.service.ts
export class HeroService {}
</code-example>
Without a barrel, a consumer needs three import statements:
<code-example>
import { HeroComponent } from '../heroes/hero.component.ts';
import { Hero } from '../heroes/hero.model.ts';
import { HeroService } from '../heroes/hero.service.ts';
</code-example>
You can add a barrel to the `heroes` folder (called `index`, by convention) that exports all of these items:
<code-example>
export * from './hero.model.ts'; // re-export all of its exports
export * from './hero.service.ts'; // re-export all of its exports
export { HeroComponent } from './hero.component.ts'; // re-export the named thing
</code-example>
Now a consumer can import what it needs from the barrel.
<code-example>
import { Hero, HeroService } from '../heroes'; // index is implied
</code-example>
The Angular [scoped packages](guide/glossary#scoped-package) each have a barrel named `index`.
~~~ {.alert.is-important}
You can often achieve the same result using [Angular modules](guide/glossary#angular-module) instead.
~~~
~~~
## Binding ## Binding
~~~ {.l-sub-section} ~~~ {.l-sub-section}
@ -78,7 +149,7 @@ between a "token"&mdash;also referred to as a "key"&mdash;and a dependency [prov
You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`). You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`).
Bootstrapping identifies an application's top level "root" [component](guide/glossary#component), Bootstrapping identifies an application's top level "root" [component](guide/glossary#component),
which is the first component that is loaded for the application. which is the first component that is loaded for the application.
For more information, see the [Setup](guide/!{docsLatest}/guide/setup) page. For more information, see the [Setup](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.
@ -113,7 +184,7 @@ An Angular class responsible for exposing data to a [view](guide/glossary#view)
The *component* is one of the most important building blocks in the Angular system. The *component* is one of the most important building blocks in the Angular system.
It is, in fact, an Angular [directive](guide/glossary#directive) with a companion [template](guide/glossary#template). It is, in fact, an Angular [directive](guide/glossary#directive) with a companion [template](guide/glossary#template).
Apply the `!{_at}Component` !{_decoratorLink} to Apply the `@Component` [decorator](guide/glossary#decorator) to
the component class, thereby attaching to the class the essential component metadata the component class, thereby attaching to the class the essential component metadata
that Angular needs to create a component instance and render the component with its template that Angular needs to create a component instance and render the component with its template
as a view. as a view.
@ -132,8 +203,8 @@ the component in the role of "controller" or "view model".
The practice of writing compound words or phrases such that each word is separated by a dash or hyphen (`-`). The practice of writing compound words or phrases such that each word is separated by a dash or hyphen (`-`).
This form is also known as kebab-case. This form is also known as kebab-case.
[Directive](guide/glossary#directive) selectors (like `my-app`) <span if-docs="ts">and [Directive](guide/glossary#directive) selectors (like `my-app`) and
the root of filenames (such as `hero-list.component.ts`)</span> are often the root of filenames (such as `hero-list.component.ts`) are often
spelled in dash-case. spelled in dash-case.
@ -155,14 +226,14 @@ updating application data values.
Angular has a rich data-binding framework with a variety of data-binding Angular has a rich data-binding framework with a variety of data-binding
operations and supporting declaration syntax. operations and supporting declaration syntax.
Read about the following forms of binding in the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page: Read about the following forms of binding in the [Template Syntax](guide/template-syntax) page:
* [Interpolation](guide/!{docsLatest}/guide/template-syntax). * [Interpolation](guide/template-syntax).
* [Property binding](guide/!{docsLatest}/guide/template-syntax). * [Property binding](guide/template-syntax).
* [Event binding](guide/!{docsLatest}/guide/template-syntax). * [Event binding](guide/template-syntax).
* [Attribute binding](guide/!{docsLatest}/guide/template-syntax). * [Attribute binding](guide/template-syntax).
* [Class binding](guide/!{docsLatest}/guide/template-syntax). * [Class binding](guide/template-syntax).
* [Style binding](guide/!{docsLatest}/guide/template-syntax). * [Style binding](guide/template-syntax).
* [Two-way data binding with ngModel](guide/!{docsLatest}/guide/template-syntax). * [Two-way data binding with ngModel](guide/template-syntax).
~~~ ~~~
@ -261,7 +332,7 @@ Registering providers is a critical preparatory step.
Angular registers some of its own providers with every injector. Angular registers some of its own providers with every injector.
You can register your own providers. You can register your own providers.
Read more in the [Dependency Injection](guide/!{docsLatest}/guide/dependency-injection) page. Read more in the [Dependency Injection](guide/dependency-injection) page.
~~~ ~~~
@ -381,11 +452,11 @@ with a registered [provider](guide/glossary#provider).
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A directive property that can be the *target* of a A directive property that can be the *target* of a
[property binding](guide/!{docsLatest}/guide/template-syntax) (explained in detail in the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page). [property binding](guide/template-syntax) (explained in detail in the [Template Syntax](guide/template-syntax) page).
Data values flow *into* this property from the data source identified Data values flow *into* this property from the data source identified
in the template expression to the right of the equal sign. in the template expression to the right of the equal sign.
See the [Input and output properties](guide/!{docsLatest}/guide/template-syntax) section of the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page. See the [Input and output properties](guide/template-syntax) section of the [Template Syntax](guide/template-syntax) page.
~~~ ~~~
@ -406,8 +477,8 @@ or displayed between element tags, as in this example.
</code-example> </code-example>
Read more about [interpolation](guide/!{docsLatest}/guide/template-syntax) in the Read more about [interpolation](guide/template-syntax) in the
[Template Syntax](guide/!{docsLatest}/guide/template-syntax) page. [Template Syntax](guide/template-syntax) page.
~~~ ~~~
@ -420,7 +491,7 @@ Read more about [interpolation](guide/!{docsLatest}/guide/template-syntax) in th
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A bootstrapping method of compiling components<span if-docs="ts"> and modules</span> in the browser A bootstrapping method of compiling components and modules in the browser
and launching the application dynamically. Just-in-time mode is a good choice during development. and launching the application dynamically. Just-in-time mode is a good choice during development.
Consider using the [ahead-of-time](guide/glossary#aot) mode for production apps. Consider using the [ahead-of-time](guide/glossary#aot) mode for production apps.
@ -461,7 +532,7 @@ Angular calls these hook methods in the following order:
* `ngAfterViewChecked`: after every check of a component's views. * `ngAfterViewChecked`: after every check of a component's views.
* `ngOnDestroy`: just before the directive is destroyed. * `ngOnDestroy`: just before the directive is destroyed.
Read more in the [Lifecycle Hooks](guide/!{docsLatest}/guide/lifecycle-hooks) page. Read more in the [Lifecycle Hooks](guide/lifecycle-hooks) page.
~~~ ~~~
@ -477,7 +548,7 @@ Read more in the [Lifecycle Hooks](guide/!{docsLatest}/guide/lifecycle-hooks) pa
Angular has the following types of modules: Angular has the following types of modules:
- [Angular modules](guide/glossary#angular-module). - [Angular modules](guide/glossary#angular-module).
For details and examples, see the [Angular Modules](guide/!{docsLatest}/guide/ngmodule) page. For details and examples, see the [Angular Modules](guide/ngmodule) page.
- ES2015 modules, as described in this section. - ES2015 modules, as described in this section.
@ -514,17 +585,31 @@ You rarely access Angular feature modules directly. You usually import them from
{@a N} {@a N}
## Observable
~~~ {.l-sub-section}
An array whose items arrive asynchronously over time.
Observables help you manage asynchronous data, such as data coming from a backend service.
Observables are used within Angular itself, including Angular's event system and its HTTP client service.
To use observables, Angular uses a third-party library called Reactive Extensions (RxJS).
Observables are a proposed feature for ES2016, the next version of JavaScript.
~~~
## Output ## Output
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A directive property that can be the *target* of event binding A directive property that can be the *target* of event binding
(read more in the [event binding](guide/!{docsLatest}/guide/template-syntax) (read more in the [event binding](guide/template-syntax)
section of the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page). section of the [Template Syntax](guide/template-syntax) page).
Events stream *out* of this property to the receiver identified Events stream *out* of this property to the receiver identified
in the template expression to the right of the equal sign. in the template expression to the right of the equal sign.
See the [Input and output properties](guide/!{docsLatest}/guide/template-syntax) section of the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page. See the [Input and output properties](guide/template-syntax) section of the [Template Syntax](guide/template-syntax) page.
~~~ ~~~
@ -559,7 +644,7 @@ a numeric value in the local currency.
</code-example> </code-example>
You can also write your own custom pipes. You can also write your own custom pipes.
Read more in the page on [pipes](guide/!{docsLatest}/guide/pipes). Read more in the page on [pipes](guide/pipes).
~~~ ~~~
@ -609,6 +694,18 @@ replace one view with another.
The Angular component router is a richly featured mechanism for configuring and managing the entire view navigation process, including the creation and destruction The Angular component router is a richly featured mechanism for configuring and managing the entire view navigation process, including the creation and destruction
of views. of views.
In most cases, components become attached to a router by means
of a `RouterConfig` that defines routes to views.
A [routing component's](guide/glossary#routing-component) template has a `RouterOutlet` element
where it can display views produced by the router.
Other views in the application likely have anchor tags or buttons with `RouterLink`
directives that users can click to navigate.
For more information, see the [Routing & Navigation](guide/router) page.
~~~ ~~~
## Router module ## Router module
@ -617,7 +714,7 @@ of views.
A separate [Angular module](guide/glossary#angular-module) that provides the necessary service providers and directives for navigating through application views. A separate [Angular module](guide/glossary#angular-module) that provides the necessary service providers and directives for navigating through application views.
For more information, see the [Routing & Navigation](guide/!{docsLatest}/guide/router) page. For more information, see the [Routing & Navigation](guide/router) page.
~~~ ~~~
@ -628,7 +725,7 @@ For more information, see the [Routing & Navigation](guide/!{docsLatest}/guide/r
An Angular [component](guide/glossary#component) with a `RouterOutlet` that displays views based on router navigations. An Angular [component](guide/glossary#component) with a `RouterOutlet` that displays views based on router navigations.
For more information, see the [Routing & Navigation](guide/!{docsLatest}/guide/router) page. For more information, see the [Routing & Navigation](guide/router) page.
~~~ ~~~
@ -649,7 +746,6 @@ The only difference, from a consumer perspective,
is that the scoped package name begins with the Angular *scope name*, `@angular`. 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 path="architecture/src/app/app.component.ts" linenums="false" title="architecture/ts/src/app/app.component.ts (import)" region="import">
</code-example> </code-example>
@ -674,7 +770,7 @@ provide shared data or logic across components, or encapsulate external interact
Applications often require services such as a data service or a logging service. Applications often require services such as a data service or a logging service.
For more information, see the [Services](guide/!{docsLatest}/tutorial/toh-pt4) page of the [Tour of Heroes](guide/!{docsLatest}/tutorial/) tutorial. For more information, see the [Services](guide/.ial/toh-pt4) page of the [Tour of Heroes](guide/.ial/) tutorial.
~~~ ~~~
@ -706,7 +802,7 @@ A category of [directive](guide/glossary#directive) that can
shape or reshape HTML layout, typically by adding and removing elements in the DOM. shape or reshape HTML layout, typically by adding and removing elements in the DOM.
The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are well-known examples. The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are well-known examples.
Read more in the [Structural Directives](guide/!{docsLatest}/guide/structural-directives) page. Read more in the [Structural Directives](guide/structural-directives) page.
~~~ ~~~
@ -739,7 +835,7 @@ When building template-driven forms:
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data-entry form scenarios. 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 Read about how to build template-driven forms
in the [Forms](guide/!{docsLatest}/guide/forms) page. in the [Forms](guide/forms) page.
~~~ ~~~
@ -748,12 +844,12 @@ in the [Forms](guide/!{docsLatest}/guide/forms) page.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A !{_Lang}-like syntax that Angular evaluates within A TypeScript-like syntax that Angular evaluates within
a [data binding](guide/glossary#data-binding). a [data binding](guide/glossary#data-binding).
Read about how to write template expressions Read about how to write template expressions
in the [Template expressions](guide/!{docsLatest}/guide/template-syntax) section in the [Template expressions](guide/template-syntax) section
of the [Template Syntax](guide/!{docsLatest}/guide/template-syntax) page. of the [Template Syntax](guide/template-syntax) page.
~~~ ~~~

View File

@ -79,7 +79,7 @@ Thus, a provider in an intermediate injector intercepts a request for a service
It effectively "reconfigures" and "shadows" a provider at a higher level in the tree. It effectively "reconfigures" and "shadows" a provider at a higher level in the tree.
If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat. If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
All requests bubble up to the root <span if-docs="ts"><code>NgModule</code></span> injector that you configured with the `!{_bootstrapModule}` method. All requests bubble up to the root <code>NgModule</code> injector that you configured with the `bootstrapModule` method.
## Component injectors ## Component injectors
@ -101,7 +101,6 @@ That's not supposed to happen but providing the service in the root `AppModule`
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this: Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata"> <code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata">
</code-example> </code-example>
@ -145,7 +144,6 @@ It caches a single `HeroTaxReturn`, tracks changes to that return, and can save
It also delegates to the application-wide singleton `HeroService`, which it gets by injection. It also delegates to the application-wide singleton `HeroService`, which it gets by injection.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts">
</code-example> </code-example>
@ -153,7 +151,6 @@ It also delegates to the application-wide singleton `HeroService`, which it gets
Here is the `HeroTaxReturnComponent` that makes use of it. Here is the `HeroTaxReturnComponent` that makes use of it.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts">
</code-example> </code-example>
@ -171,7 +168,6 @@ What a mess!
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property. Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers"> <code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example> </code-example>

View File

@ -85,7 +85,6 @@ After translation, the compiler removes it.
In the accompanying sample, an `<h1>` tag displays a simple English language greeting In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish: that you translate into Spanish:
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false">
</code-example> </code-example>
@ -93,7 +92,6 @@ that you translate into Spanish:
Add the `i18n` attribute to the tag to mark it for translation. Add the `i18n` attribute to the tag to mark it for translation.
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false">
</code-example> </code-example>
@ -105,7 +103,6 @@ need a description of the message.
Assign a description to the i18n attribute: Assign a description to the i18n attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false">
</code-example> </code-example>
@ -117,7 +114,6 @@ In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`): separating it from the description with the `|` character (`<meaning>|<description>`):
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false">
</code-example> </code-example>
@ -138,7 +134,6 @@ Here are two techniques to try.
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered: (1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false">
</code-example> </code-example>
@ -146,7 +141,6 @@ Here are two techniques to try.
(2) Wrap the text in a pair of HTML comments: (2) Wrap the text in a pair of HTML comments:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false">
</code-example> </code-example>
@ -159,7 +153,6 @@ Here are two techniques to try.
You've added an image to your template. You care about accessibility too so you add a `title` attribute: You've added an image to your template. You care about accessibility too so you add a `title` attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false"> <code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false">
</code-example> </code-example>
@ -171,7 +164,6 @@ name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write: To translate the `title` on the `img` tag from the previous example, write:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false">
</code-example> </code-example>
@ -192,7 +184,6 @@ Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves: Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false">
</code-example> </code-example>
@ -249,7 +240,6 @@ property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation: The message maps those values to the appropriate translation:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example> </code-example>
@ -394,7 +384,6 @@ This sample file is easy to translate without a special editor or knowledge of S
Open `messages.es.xlf` and find the first `<trans-unit>` section: Open `messages.es.xlf` and find the first `<trans-unit>` section:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example> </code-example>
@ -404,7 +393,6 @@ This XML element represents the translation of the `<h1>` greeting tag you marke
Using the _source_, _description_, and _meaning_ elements to guide your translation, Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting: replace the `<target/>` tag with the Spanish greeting:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
</code-example> </code-example>
@ -424,7 +412,6 @@ See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
Translate the other text nodes the same way: Translate the other text nodes the same way:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false">
</code-example> </code-example>
@ -446,7 +433,6 @@ In this example, you know the translation unit for the `select` must be just bel
To translate a `plural`, translate its ICU format match values: To translate a `plural`, translate its ICU format match values:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false">
</code-example> </code-example>
@ -455,7 +441,6 @@ To translate a `plural`, translate its ICU format match values:
The `select` behaves a little differently. Here again is the ICU format message in the component template: The `select` behaves a little differently. Here again is the ICU format message in the component template:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false"> <code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
</code-example> </code-example>
@ -467,7 +452,6 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and leave the placeholder where it is. Translate the text and leave the placeholder where it is.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false">
</code-example> </code-example>
@ -475,7 +459,6 @@ Translate the text and leave the placeholder where it is.
The second translation unit, immediately below the first one, contains the `select` message. Translate that. The second translation unit, immediately below the first one, contains the `select` message. Translate that.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false">
</code-example> </code-example>
@ -483,7 +466,6 @@ The second translation unit, immediately below the first one, contains the `sele
Here they are together, after translation: Here they are together, after translation:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false"> <code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false">
</code-example> </code-example>
@ -507,39 +489,28 @@ time to incorporate that translation into the application.
When the previous steps finish, the sample app _and_ its translation file are as follows: When the previous steps finish, the sample app _and_ its translation file are as follows:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="cb-i18n/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-i18n/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="cb-i18n/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts"> <code-pane title="src/main.ts" path="cb-i18n/src/main.1.ts">
</code-pane> </code-pane>
<code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html"> <code-pane title="src/locale/messages.es.xlf" path="cb-i18n/src/locale/messages.es.xlf.html">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -580,7 +551,6 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows: Open `index.html` and revise the launch script as follows:
<code-example path="cb-i18n/src/index.html" region="i18n" linenums="false"> <code-example path="cb-i18n/src/index.html" region="i18n" linenums="false">
</code-example> </code-example>
@ -600,7 +570,6 @@ You'll need it to import the language translation file.
SystemJS doesn't ship with a raw text plugin but it's easy to add. SystemJS doesn't ship with a raw text plugin but it's easy to add.
Create the following `systemjs-text-plugin.js` in the `src/` folder: Create the following `systemjs-text-plugin.js` in the `src/` folder:
<code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false"> <code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false">
</code-example> </code-example>
@ -618,7 +587,6 @@ The `getTranslationProviders` function in the following `src/app/i18n-providers.
creates those providers based on the user's _locale_ creates those providers based on the user's _locale_
and the corresponding translation file: and the corresponding translation file:
<code-example path="cb-i18n/src/app/i18n-providers.ts"> <code-example path="cb-i18n/src/app/i18n-providers.ts">
</code-example> </code-example>
@ -648,7 +616,6 @@ You'll create an _options_ object with the translation providers from `getTransl
and pass it to `bootstrapModule`. and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows: Open the `src/main.ts` and modify the bootstrap code as follows:
<code-example path="cb-i18n/src/main.ts" linenums="false"> <code-example path="cb-i18n/src/main.ts" linenums="false">
</code-example> </code-example>

View File

@ -18,131 +18,91 @@ a collection of pages devoted to that theme.
<table width="100%"> <table width="100%">
<col width="15%"> <col width="15%">
</col> </col>
<col> <col>
</col> </col>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../quickstart.html">QuickStart</a></b> <b><a href="../quickstart.html">QuickStart</a></b>
</td> </td>
<td> <td>
A first taste of Angular<span if-docs="ts"> with zero installation. A first taste of Angular<span if-docs="ts"> with zero installation.
Run "Hello World" in an online code editor and start playing with live code</span>. Run "Hello World" in an online code editor and start playing with live code</span>.
</td> </td>
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b>Guide</b> <b>Guide</b>
</td> </td>
<td> <td>
Learn the Angular basics (you're already here!) like the setup for local development, Learn the Angular basics (you're already here!) like the setup for local development,
displaying data and accepting user input, injecting application services into components, displaying data and accepting user input, injecting application services into components,
and building simple forms. and building simple forms.
</td> </td>
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../api/">API Reference</a></b> <b><a href="../api/">API Reference</a></b>
</td> </td>
<td> <td>
Authoritative details about each of the Angular libraries. Authoritative details about each of the Angular libraries.
</td> </td>
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href="../tutorial/">Tutorial</a></b> <b><a href="../tutorial/">Tutorial</a></b>
</td> </td>
<td> <td>
A step-by-step, immersive approach to learning Angular that A step-by-step, immersive approach to learning Angular that
introduces the major features of Angular in an application context. introduces the major features of Angular in an application context.
</td> </td>
</tr> </tr>
<tr style=top> <tr style=top>
<td> <td>
<b><a href=" ">Advanced</a></b> <b><a href=" ">Advanced</a></b>
</td> </td>
<td> <td>
In-depth analysis of Angular features and development practices. In-depth analysis of Angular features and development practices.
</td> </td>
</tr> </tr>
<tr style=top if-docs="ts"> <tr style=top if-docs="ts">
<td> <td>
<b><a href="../cookbook/">Cookbook</a></b> <b><a href="../cookbook/">Cookbook</a></b>
</td> </td>
<td> <td>
Recipes for specific application challenges, mostly code snippets with a minimum of exposition. Recipes for specific application challenges, mostly code snippets with a minimum of exposition.
</td> </td>
</tr> </tr>
</table> </table>
A few early pages are written as tutorials and are clearly marked as such. A few early pages are written as tutorials and are clearly marked as such.

View File

@ -21,7 +21,26 @@ Angular offers **lifecycle hooks**
that provide visibility into these key life moments and the ability to act when they occur. that provide visibility into these key life moments and the ability to act when they occur.
A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views. A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views.
<br class="l-clear-both">Try the <live-example></live-example>. <br class="l-clear-both">
## Contents
* [Component lifecycle hooks overview](guide/lifecycle-hooks#hooks-overview)
* [Lifecycle sequence](guide/lifecycle-hooks#hooks-purpose-timing)
* [Interfaces are optional (technically)](guide/lifecycle-hooks#interface-optional)
* [Other Angular lifecycle hooks](guide/lifecycle-hooks#other-lifecycle-hooks)
* [Lifecycle examples](guide/lifecycle-hooks#the-sample)
* [Peek-a-boo: all hooks](guide/lifecycle-hooks#peek-a-boo)
* [Spying OnInit and OnDestroy](guide/lifecycle-hooks#spy)
* [OnInit](guide/lifecycle-hooks#oninit)
* [OnDestroy](guide/lifecycle-hooks#ondestroy)
* [OnChanges](guide/lifecycle-hooks#onchanges)
* [DoCheck](guide/lifecycle-hooks#docheck)
* [AfterView](guide/lifecycle-hooks#afterview)
* [Abide by the unidirectional data flow rule](guide/lifecycle-hooks#wait-a-tick)
* [AfterContent](guide/lifecycle-hooks#aftercontent)
* [Content projection](guide/lifecycle-hooks#content-projection)
* [AfterContent hooks](guide/lifecycle-hooks#aftercontent-hooks)
* [No unidirectional flow worries with _AfterContent_](guide/lifecycle-hooks#no-unidirectional-flow-worries)
Try the <live-example></live-example>.
{@a hooks-overview} {@a hooks-overview}
@ -36,7 +55,6 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit()` For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component: that Angular calls shortly after creating the component:
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false"> <code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false">
</code-example> </code-example>
@ -53,46 +71,32 @@ calls the lifecycle hook methods in the following sequence at specific moments:
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
Hook Hook
</th> </th>
<th> <th>
Purpose and Timing Purpose and Timing
</th> </th>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngOnChanges()</code> <code>ngOnChanges()</code>
</td> </td>
<td> <td>
Respond when Angular (re)sets data-bound input properties. Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values. The method receives a `SimpleChanges` object of current and previous property values.
@ -101,20 +105,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngOnInit()</code> <code>ngOnInit()</code>
</td> </td>
<td> <td>
Initialize the directive/component after Angular first displays the data-bound properties Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties. and sets the directive/component's input properties.
@ -123,20 +121,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngDoCheck()</code> <code>ngDoCheck()</code>
</td> </td>
<td> <td>
Detect and act upon changes that Angular can't or won't detect on its own. Detect and act upon changes that Angular can't or won't detect on its own.
@ -144,20 +136,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngAfterContentInit()</code> <code>ngAfterContentInit()</code>
</td> </td>
<td> <td>
Respond after Angular projects external content into the component's view. Respond after Angular projects external content into the component's view.
@ -167,20 +153,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngAfterContentChecked()</code> <code>ngAfterContentChecked()</code>
</td> </td>
<td> <td>
Respond after Angular checks the content projected into the component. Respond after Angular checks the content projected into the component.
@ -190,20 +170,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngAfterViewInit()</code> <code>ngAfterViewInit()</code>
</td> </td>
<td> <td>
Respond after Angular initializes the component's views and child views. Respond after Angular initializes the component's views and child views.
@ -213,20 +187,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngAfterViewChecked()</code> <code>ngAfterViewChecked()</code>
</td> </td>
<td> <td>
Respond after Angular checks the component's views and child views. Respond after Angular checks the component's views and child views.
@ -236,20 +204,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<code>ngOnDestroy</code> <code>ngOnDestroy</code>
</td> </td>
<td> <td>
Cleanup just before Angular destroys the directive/component. Cleanup just before Angular destroys the directive/component.
Unsubscribe Observables and detach event handlers to avoid memory leaks. Unsubscribe Observables and detach event handlers to avoid memory leaks.
@ -258,20 +220,35 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td> </td>
</tr> </tr>
</table> </table>
{@a interface-optional}
## Interfaces are optional (technically)
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
The JavaScript language doesn't have interfaces.
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
Fortunately, they aren't necessary.
You don't have to add the lifecycle hook interfaces to directives and components to benefit from the hooks themselves.
Angular instead inspects directive and component classes and calls the hook methods *if they are defined*.
Angular finds and calls methods like `ngOnInit()`, with or without the interfaces.
Nonetheless, it's good practice to add interfaces to TypeScript directive classes
in order to benefit from strong typing and editor tooling.
{@a other-lifecycle-hooks} {@a other-lifecycle-hooks}
## Other Angular lifecycle hooks ## Other Angular lifecycle hooks
Other Angular sub-systems may have their own lifecycle hooks apart from these component hooks. Other Angular sub-systems may have their own lifecycle hooks apart from these component hooks.
3rd party libraries might implement their hooks as well in order to give developers more 3rd party libraries might implement their hooks as well in order to give developers more
control over how these libraries are used. control over how these libraries are used.
@ -289,65 +266,45 @@ Here's a brief description of each exercise:
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
Component Component
</th> </th>
<th> <th>
Description Description
</th> </th>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#peek-a-boo">Peek-a-boo</a> <a href="#peek-a-boo">Peek-a-boo</a>
</td> </td>
<td> <td>
Demonstrates every lifecycle hook. Demonstrates every lifecycle hook.
Each hook method writes to the on-screen log. Each hook method writes to the on-screen log.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#spy">Spy</a> <a href="#spy">Spy</a>
</td> </td>
<td> <td>
Directives have lifecycle hooks too. Directives have lifecycle hooks too.
A `SpyDirective` can log when the element it spies upon is A `SpyDirective` can log when the element it spies upon is
@ -357,98 +314,68 @@ Here's a brief description of each exercise:
managed by the parent `SpyComponent`. managed by the parent `SpyComponent`.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#onchanges">OnChanges</a> <a href="#onchanges">OnChanges</a>
</td> </td>
<td> <td>
See how Angular calls the `ngOnChanges()` hook with a `changes` object See how Angular calls the `ngOnChanges()` hook with a `changes` object
every time one of the component input properties changes. every time one of the component input properties changes.
Shows how to interpret the `changes` object. Shows how to interpret the `changes` object.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#docheck">DoCheck</a> <a href="#docheck">DoCheck</a>
</td> </td>
<td> <td>
Implements an `ngDoCheck()` method with custom change detection. Implements an `ngDoCheck()` method with custom change detection.
See how often Angular calls this hook and watch it post changes to a log. See how often Angular calls this hook and watch it post changes to a log.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#afterview">AfterView</a> <a href="#afterview">AfterView</a>
</td> </td>
<td> <td>
Shows what Angular means by a *view*. Shows what Angular means by a *view*.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks. Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
<a href="#aftercontent">AfterContent</a> <a href="#aftercontent">AfterContent</a>
</td> </td>
<td> <td>
Shows how to project external content into a component and Shows how to project external content into a component and
how to distinguish projected content from a component's view children. how to distinguish projected content from a component's view children.
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks. Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
</td> </td>
</tr> </tr>
<tr style='vertical-align:top'>
<tr style=top>
<td> <td>
Counter Counter
</td> </td>
<td> <td>
Demonstrates a combination of a component and a directive Demonstrates a combination of a component and a directive
each with its own hooks. each with its own hooks.
@ -460,10 +387,8 @@ Here's a brief description of each exercise:
</td> </td>
</tr> </tr>
</table> </table>
The remainder of this page discusses selected exercises in further detail. The remainder of this page discusses selected exercises in further detail.
@ -531,7 +456,6 @@ The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` a
that log messages to the parent via an injected `LoggerService`. that log messages to the parent via an injected `LoggerService`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false"> <code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false">
</code-example> </code-example>
@ -540,7 +464,6 @@ You can apply the spy to any native or component element and it'll be initialize
at the same time as that element. at the same time as that element.
Here it is attached to the repeated hero `<div>`: Here it is attached to the repeated hero `<div>`:
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false">
</code-example> </code-example>
@ -625,7 +548,6 @@ You risk memory leaks if you neglect to do so.
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive). Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook. This example monitors the `OnChanges` hook.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false"> <code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false">
</code-example> </code-example>
@ -636,7 +558,6 @@ This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`. The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false"> <code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false">
</code-example> </code-example>
@ -644,7 +565,6 @@ The example component, `OnChangesComponent`, has two input properties: `hero` an
The host `OnChangesParentComponent` binds to them like this: The host `OnChangesParentComponent` binds to them like this:
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes"> <code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes">
</code-example> </code-example>
@ -679,7 +599,6 @@ Use this method to detect a change that Angular overlooked.
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook: The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false"> <code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false">
</code-example> </code-example>
@ -712,14 +631,12 @@ The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` h
Here's a child view that displays a hero's name in an `<input>`: Here's a child view that displays a hero's name in an `<input>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false">
</code-example> </code-example>
The `AfterViewComponent` displays this child view *within its template*: The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -729,7 +646,6 @@ which can only be reached by querying for the child view via the property decora
[@ViewChild](api/core/index/ViewChild-decorator). [@ViewChild](api/core/index/ViewChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false">
</code-example> </code-example>
@ -741,7 +657,6 @@ which can only be reached by querying for the child view via the property decora
The `doSomething()` method updates the screen when the hero name exceeds 10 characters. The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false">
</code-example> </code-example>
@ -788,7 +703,6 @@ Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afte
This time, instead of including the child view within the template, it imports the content from This time, instead of including the child view within the template, it imports the content from
the `AfterContentComponent`'s parent. Here's the parent's template: the `AfterContentComponent`'s parent. Here's the parent's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false">
</code-example> </code-example>
@ -799,7 +713,6 @@ into the component*.
Now look at the component's template: Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -840,7 +753,6 @@ which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/index/ContentChild-decorator). [@ContentChild](api/core/index/ContentChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false"> <code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false">
</code-example> </code-example>

View File

@ -21,6 +21,7 @@ These FAQs assume that you have read the [NgModules](guide/ngmodule) page.
~~~ ~~~
Declarations Declarations
* [What classes should I add to _declarations_?](guide/ngmodule-faq#q-what-to-declare) * [What classes should I add to _declarations_?](guide/ngmodule-faq#q-what-to-declare)
* [What is a _declarable_?](guide/ngmodule-faq#q-declarable) * [What is a _declarable_?](guide/ngmodule-faq#q-declarable)
* [What classes should I _not_ add to _declarations_?](guide/ngmodule-faq#q-what-not-to-declare) * [What classes should I _not_ add to _declarations_?](guide/ngmodule-faq#q-what-not-to-declare)
@ -28,17 +29,20 @@ Declarations
* [What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?](guide/ngmodule-faq#q-why-cant-bind-to) * [What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?](guide/ngmodule-faq#q-why-cant-bind-to)
Imports Imports
* [What should I import?](guide/ngmodule-faq#q-what-to-import) * [What should I import?](guide/ngmodule-faq#q-what-to-import)
* [Should I import _BrowserModule_ or _CommonModule_?](guide/ngmodule-faq#q-browser-vs-common-module) * [Should I import _BrowserModule_ or _CommonModule_?](guide/ngmodule-faq#q-browser-vs-common-module)
* [What if I import the same module twice?](guide/ngmodule-faq#q-reimport) * [What if I import the same module twice?](guide/ngmodule-faq#q-reimport)
Exports Exports
* [What should I export?](guide/ngmodule-faq#q-what-to-export) * [What should I export?](guide/ngmodule-faq#q-what-to-export)
* [What should I *not* export?](guide/ngmodule-faq#q-what-not-to-export) * [What should I *not* export?](guide/ngmodule-faq#q-what-not-to-export)
* [Can I re-export imported classes and modules?](guide/ngmodule-faq#q-re-export) * [Can I re-export imported classes and modules?](guide/ngmodule-faq#q-re-export)
* [What is the _forRoot_ method?](guide/ngmodule-faq#q-for-root) * [What is the _forRoot_ method?](guide/ngmodule-faq#q-for-root)
Service Providers Service Providers
* [Why is a service provided in a feature module visible everywhere?](guide/ngmodule-faq#q-module-provider-visibility) * [Why is a service provided in a feature module visible everywhere?](guide/ngmodule-faq#q-module-provider-visibility)
* [Why is a service provided in a _lazy-loaded_ module visible only to that module?](guide/ngmodule-faq#q-lazy-loaded-module-provider-visibility) * [Why is a service provided in a _lazy-loaded_ module visible only to that module?](guide/ngmodule-faq#q-lazy-loaded-module-provider-visibility)
* [What if two modules provide the same service?](guide/ngmodule-faq#q-module-provider-duplicates) * [What if two modules provide the same service?](guide/ngmodule-faq#q-module-provider-duplicates)
@ -50,12 +54,14 @@ Service Providers
* [How can I tell if a module or service was previously loaded?](guide/ngmodule-faq#q-is-it-loaded) * [How can I tell if a module or service was previously loaded?](guide/ngmodule-faq#q-is-it-loaded)
Entry Components Entry Components
* [What is an _entry component_?](guide/ngmodule-faq#q-entry-component-defined) * [What is an _entry component_?](guide/ngmodule-faq#q-entry-component-defined)
* [What is the difference between a _bootstrap_ component and an _entry component_?](guide/ngmodule-faq#q-bootstrap_vs_entry_component) * [What is the difference between a _bootstrap_ component and an _entry component_?](guide/ngmodule-faq#q-bootstrap_vs_entry_component)
* [When do I add components to _entryComponents_?](guide/ngmodule-faq#q-when-entry-components) * [When do I add components to _entryComponents_?](guide/ngmodule-faq#q-when-entry-components)
* [Why does Angular need _entryComponents_?](guide/ngmodule-faq#q-why-entry-components) * [Why does Angular need _entryComponents_?](guide/ngmodule-faq#q-why-entry-components)
General General
* [What kinds of modules should I have and how should I use them?](guide/ngmodule-faq#q-module-recommendations) * [What kinds of modules should I have and how should I use them?](guide/ngmodule-faq#q-module-recommendations)
* [What's the difference between Angular and JavaScript Modules?](guide/ngmodule-faq#q-ng-vs-js-modules) * [What's the difference between Angular and JavaScript Modules?](guide/ngmodule-faq#q-ng-vs-js-modules)
* [How does Angular find components, directives, and pipes in a template?](guide/ngmodule-faq#q-template-reference) * [How does Angular find components, directives, and pipes in a template?](guide/ngmodule-faq#q-template-reference)
@ -672,7 +678,6 @@ You can throw an error or take other remedial action.
Certain NgModules (such as `BrowserModule`) implement such a guard, Certain NgModules (such as `BrowserModule`) implement such a guard,
such as this `CoreModule` constructor from the NgModules page. such as this `CoreModule` constructor from the NgModules page.
<code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -885,34 +890,24 @@ follow them unless you have a good reason to do otherwise.
<table> <table>
<tr> <tr>
<th style="vertical-align: top"> <th style="vertical-align: top">
Feature Module Feature Module
</th> </th>
<th style="vertical-align: top"> <th style="vertical-align: top">
Guidelines Guidelines
</th> </th>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="domain-feature-module"></a>Domain <a id="domain-feature-module"></a>Domain
</td> </td>
<td> <td>
Domain feature modules deliver a user experience *dedicated to a particular application domain* Domain feature modules deliver a user experience *dedicated to a particular application domain*
like editing a customer or placing an order. like editing a customer or placing an order.
@ -944,20 +939,14 @@ follow them unless you have a good reason to do otherwise.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="routed-feature-module"></a>Routed <a id="routed-feature-module"></a>Routed
</td> </td>
<td> <td>
_Routed feature modules_ are _domain feature modules_ _Routed feature modules_ are _domain feature modules_
whose top components are the *targets of router navigation routes*. whose top components are the *targets of router navigation routes*.
@ -985,20 +974,14 @@ follow them unless you have a good reason to do otherwise.
or in a module that the routed module imports. or in a module that the routed module imports.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="routing-module"></a>Routing <a id="routing-module"></a>Routing
</td> </td>
<td> <td>
A [routing module](guide/router) *provides routing configuration* for another module. A [routing module](guide/router) *provides routing configuration* for another module.
@ -1041,20 +1024,14 @@ follow them unless you have a good reason to do otherwise.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="service-feature-module"></a>Service <a id="service-feature-module"></a>Service
</td> </td>
<td> <td>
Service modules *provide utility services* such as data access and messaging. Service modules *provide utility services* such as data access and messaging.
@ -1067,20 +1044,14 @@ follow them unless you have a good reason to do otherwise.
If you deviate from this guideline, know what you're doing and why. If you deviate from this guideline, know what you're doing and why.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<a id="widget-feature-module"></a>Widget <a id="widget-feature-module"></a>Widget
</td> </td>
<td> <td>
A widget module makes *components, directives, and pipes* available to external modules. A widget module makes *components, directives, and pipes* available to external modules.
@ -1096,10 +1067,8 @@ follow them unless you have a good reason to do otherwise.
</td> </td>
</tr> </tr>
</table> </table>
The following table summarizes the key characteristics of each _feature module_ group. The following table summarizes the key characteristics of each _feature module_ group.
@ -1114,258 +1083,174 @@ Real-world modules are often hybrids that knowingly deviate from these guideline
<table> <table>
<tr> <tr>
<th> <th>
Feature Module Feature Module
</th> </th>
<th> <th>
Declarations Declarations
</th> </th>
<th> <th>
Providers Providers
</th> </th>
<th> <th>
Exports Exports
</th> </th>
<th> <th>
Imported By Imported By
</th> </th>
<th> <th>
Examples Examples
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
Domain Domain
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
Top component Top component
</td> </td>
<td> <td>
Feature, <code>AppModule</code> Feature, <code>AppModule</code>
</td> </td>
<td> <td>
<code>ContactModule</code> (before routing) <code>ContactModule</code> (before routing)
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
Routed Routed
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Nobody Nobody
</td> </td>
<td> <td>
<code>ContactModule</code>, <code>HeroModule</code>, <code>CrisisModule</code> <code>ContactModule</code>, <code>HeroModule</code>, <code>CrisisModule</code>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
Routing Routing
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Yes (Guards) Yes (Guards)
</td> </td>
<td> <td>
<code>RouterModule</code> <code>RouterModule</code>
</td> </td>
<td> <td>
Feature (for routing) Feature (for routing)
</td> </td>
<td> <td>
<code>AppRoutingModule</code>, <code>ContactRoutingModule</code>, <code>HeroRoutingModule</code> <code>AppRoutingModule</code>, <code>ContactRoutingModule</code>, <code>HeroRoutingModule</code>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
Service Service
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
No No
</td> </td>
<td> <td>
<code>AppModule</code> <code>AppModule</code>
</td> </td>
<td> <td>
<code>HttpModule</code>, <code>CoreModule</code> <code>HttpModule</code>, <code>CoreModule</code>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
Widget Widget
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Rare Rare
</td> </td>
<td> <td>
Yes Yes
</td> </td>
<td> <td>
Feature Feature
</td> </td>
<td> <td>
<code>CommonModule</code>, <code>SharedModule</code> <code>CommonModule</code>, <code>SharedModule</code>
</td> </td>
</tr> </tr>
</table> </table>
@ -1435,14 +1320,12 @@ They are available _everywhere_.
Here's an _NgModule_ class with imports, exports, and declarations. Here's an _NgModule_ class with imports, exports, and declarations.
<code-example path="ngmodule/src/app/contact/contact.module.2.ts" region="class" linenums="false"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts" region="class" linenums="false">
</code-example> </code-example>
Of course you use _JavaScript_ modules to write _Angular_ modules as seen in the complete `contact.module.ts` file: Of course you use _JavaScript_ modules to write _Angular_ modules as seen in the complete `contact.module.ts` file:
<code-example path="ngmodule/src/app/contact/contact.module.2.ts" linenums="false"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts" linenums="false">
</code-example> </code-example>
@ -1517,34 +1400,24 @@ The following table summarizes the `NgModule` metadata properties.
<table> <table>
<tr> <tr>
<th> <th>
Property Property
</th> </th>
<th> <th>
Description Description
</th> </th>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>declarations</code> <code>declarations</code>
</td> </td>
<td> <td>
A list of [declarable](guide/ngmodule-faq#q-declarable) classes, A list of [declarable](guide/ngmodule-faq#q-declarable) classes,
the *component*, *directive*, and *pipe* classes that _belong to this module_. the *component*, *directive*, and *pipe* classes that _belong to this module_.
@ -1560,20 +1433,14 @@ The following table summarizes the `NgModule` metadata properties.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>providers</code> <code>providers</code>
</td> </td>
<td> <td>
A list of dependency-injection providers. A list of dependency-injection providers.
@ -1596,20 +1463,14 @@ The following table summarizes the `NgModule` metadata properties.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>imports</code> <code>imports</code>
</td> </td>
<td> <td>
A list of supporting modules. A list of supporting modules.
@ -1628,20 +1489,14 @@ The following table summarizes the `NgModule` metadata properties.
A component template can bind with `[(ngModel)]` only after importing the Angular `FormsModule`. A component template can bind with `[(ngModel)]` only after importing the Angular `FormsModule`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>exports</code> <code>exports</code>
</td> </td>
<td> <td>
A list of declarations&mdash;*component*, *directive*, and *pipe* classes&mdash;that A list of declarations&mdash;*component*, *directive*, and *pipe* classes&mdash;that
an importing module can use. an importing module can use.
@ -1666,20 +1521,14 @@ The following table summarizes the `NgModule` metadata properties.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>bootstrap</code> <code>bootstrap</code>
</td> </td>
<td> <td>
A list of components that can be bootstrapped. A list of components that can be bootstrapped.
@ -1692,20 +1541,14 @@ The following table summarizes the `NgModule` metadata properties.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="vertical-align: top"> <td style="vertical-align: top">
<code>entryComponents</code> <code>entryComponents</code>
</td> </td>
<td> <td>
A list of components that are _not_ [referenced](guide/ngmodule-faq#q-template-reference) in a reachable component template. A list of components that are _not_ [referenced](guide/ngmodule-faq#q-template-reference) in a reachable component template.
@ -1736,9 +1579,7 @@ The following table summarizes the `NgModule` metadata properties.
`entryComponents` list yourself, either programmatically or by hand. `entryComponents` list yourself, either programmatically or by hand.
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -24,12 +24,14 @@ of creating and maintaining a single root `AppModule` for the entire application
This page covers NgModules in greater depth. This page covers NgModules in greater depth.
## Table of Contents ## Table of Contents
<!-- CF: The titling for tables of contents in the advanced chapters is inconsistent: <!-- CF: The titling for tables of contents in the advanced chapters is inconsistent:
- some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2 - some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2
- some headings are H2, some are H3 - some headings are H2, some are H3
- some pages don't have tables of contents - some pages don't have tables of contents
I didn't make changes here as I'm not sure what the correct style is. I didn't make changes here as I'm not sure what the correct style is.
--> -->
* [Angular modularity](guide/ngmodule#angular-modularity "Add structure to the app with NgModule") * [Angular modularity](guide/ngmodule#angular-modularity "Add structure to the app with NgModule")
* [The application root module](guide/ngmodule#root-module "The startup module that every app requires") * [The application root module](guide/ngmodule#root-module "The startup module that every app requires")
* [Bootstrap](guide/ngmodule#bootstrap "Launch the app in a browser with the root module as the entry point") the root module * [Bootstrap](guide/ngmodule#bootstrap "Launch the app in a browser with the root module as the entry point") the root module
@ -123,7 +125,6 @@ By convention, the *root module* class is called `AppModule` and it exists in a
The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible: The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible:
<code-example path="setup/src/app/app.module.ts" linenums="false"> <code-example path="setup/src/app/app.module.ts" linenums="false">
</code-example> </code-example>
@ -142,7 +143,6 @@ the _root component_, the top of the app's rather bare component tree.
The example `AppComponent` simply displays a data-bound title: The example `AppComponent` simply displays a data-bound title:
<code-example path="ngmodule/src/app/app.component.0.ts" linenums="false"> <code-example path="ngmodule/src/app/app.component.0.ts" linenums="false">
</code-example> </code-example>
@ -165,7 +165,6 @@ In the first, _dynamic_ option, the [Angular compiler](cookbook/ngmodule-faq)
compiles the application in the browser and then launches the app. compiles the application in the browser and then launches the app.
<code-example path="ngmodule/src/main.ts" linenums="false"> <code-example path="ngmodule/src/main.ts" linenums="false">
</code-example> </code-example>
@ -188,7 +187,6 @@ The syntax for bootstrapping the pre-compiled `AppModuleNgFactory` is similar to
the dynamic version that bootstraps the `AppModule` class. the dynamic version that bootstraps the `AppModule` class.
<code-example path="ngmodule/src/main-static.ts" linenums="false"> <code-example path="ngmodule/src/main-static.ts" linenums="false">
</code-example> </code-example>
@ -224,14 +222,12 @@ As the app evolves,
the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives) the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives)
that sets the background color of the attached element. that sets the background color of the attached element.
<code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false"> <code-example path="ngmodule/src/app/highlight.directive.ts" linenums="false">
</code-example> </code-example>
Update the `AppComponent` template to attach the directive to the title: Update the `AppComponent` template to attach the directive to the title:
<code-example path="ngmodule/src/app/app.component.1.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.1.ts" region="template" linenums="false">
</code-example> </code-example>
@ -241,7 +237,6 @@ You must declare the directive in `AppModule`.
Import the `HighlightDirective` class and add it to the module's `declarations` like this: Import the `HighlightDirective` class and add it to the module's `declarations` like this:
<code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="directive" linenums="false">
</code-example> </code-example>
@ -251,14 +246,12 @@ Import the `HighlightDirective` class and add it to the module's `declarations`
Refactor the title into its own `TitleComponent`. Refactor the title into its own `TitleComponent`.
The component's template binds to the component's `title` and `subtitle` properties like this: The component's template binds to the component's `title` and `subtitle` properties like this:
<code-example path="ngmodule/src/app/title.component.html" region="v1" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" region="v1" linenums="false">
</code-example> </code-example>
<code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false"> <code-example path="ngmodule/src/app/title.component.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -266,7 +259,6 @@ The component's template binds to the component's `title` and `subtitle` propert
Rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element, Rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element,
using an input binding to set the `subtitle`. using an input binding to set the `subtitle`.
<code-example path="ngmodule/src/app/app.component.1.ts" linenums="false"> <code-example path="ngmodule/src/app/app.component.1.ts" linenums="false">
</code-example> </code-example>
@ -274,7 +266,6 @@ using an input binding to set the `subtitle`.
Angular won't recognize the `<app-title>` tag until you declare it in `AppModule`. Angular won't recognize the `<app-title>` tag until you declare it in `AppModule`.
Import the `TitleComponent` class and add it to the module's `declarations`: Import the `TitleComponent` class and add it to the module's `declarations`:
<code-example path="ngmodule/src/app/app.module.1.ts" region="component" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="component" linenums="false">
</code-example> </code-example>
@ -300,7 +291,6 @@ accessible through a user service.
This sample application has a dummy implementation of such a `UserService`. This sample application has a dummy implementation of such a `UserService`.
<code-example path="ngmodule/src/app/user.service.ts" linenums="false"> <code-example path="ngmodule/src/app/user.service.ts" linenums="false">
</code-example> </code-example>
@ -308,7 +298,6 @@ This sample application has a dummy implementation of such a `UserService`.
The sample application should display a welcome message to the logged-in user just below the application title. The sample application should display a welcome message to the logged-in user just below the application title.
Update the `TitleComponent` template to show the welcome message below the application title. Update the `TitleComponent` template to show the welcome message below the application title.
<code-example path="ngmodule/src/app/title.component.html" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" linenums="false">
</code-example> </code-example>
@ -316,7 +305,6 @@ Update the `TitleComponent` template to show the welcome message below the appli
Update the `TitleComponent` class with a constructor that injects the `UserService` Update the `TitleComponent` class with a constructor that injects the `UserService`
and sets the component's `user` property from the service. and sets the component's `user` property from the service.
<code-example path="ngmodule/src/app/title.component.ts" linenums="false"> <code-example path="ngmodule/src/app/title.component.ts" linenums="false">
</code-example> </code-example>
@ -324,7 +312,6 @@ and sets the component's `user` property from the service.
You've defined and used the service. Now to _provide_ it for all components to use, You've defined and used the service. Now to _provide_ it for all components to use,
add it to a `providers` property in the `AppModule` metadata: add it to a `providers` property in the `AppModule` metadata:
<code-example path="ngmodule/src/app/app.module.1.ts" region="providers" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -338,7 +325,6 @@ add it to a `providers` property in the `AppModule` metadata:
In the revised `TitleComponent`, an `*ngIf` directive guards the message. In the revised `TitleComponent`, an `*ngIf` directive guards the message.
There is no message if there is no user. There is no message if there is no user.
<code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false"> <code-example path="ngmodule/src/app/title.component.html" region="ngIf" linenums="false">
</code-example> </code-example>
@ -349,7 +335,6 @@ How can that be? The Angular compiler should either ignore or complain about unr
Angular does recognize `NgIf` because you imported it earlier. Angular does recognize `NgIf` because you imported it earlier.
The initial version of `AppModule` imports `BrowserModule`. The initial version of `AppModule` imports `BrowserModule`.
<code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.0.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -403,7 +388,6 @@ import the `ReactiveFormsModule`.
The `ContactComponent` selector matches an element named `<app-contact>`. The `ContactComponent` selector matches an element named `<app-contact>`.
Add an element with that name to the `AppComponent` template, just below the `<app-title>`: Add an element with that name to the `AppComponent` template, just below the `<app-title>`:
<code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.1b.ts" region="template" linenums="false">
</code-example> </code-example>
@ -415,45 +399,32 @@ and an alternative version of the `HighlightDirective`.
To make it manageable, place all contact-related material in an `src/app/contact` folder To make it manageable, place all contact-related material in an `src/app/contact` folder
and break the component into three constituent HTML, TypeScript, and css files: and break the component into three constituent HTML, TypeScript, and css files:
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html"> <code-pane title="src/app/contact/contact.component.html" path="ngmodule/src/app/contact/contact.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts"> <code-pane title="src/app/contact/contact.component.ts" path="ngmodule/src/app/contact/contact.component.3.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css"> <code-pane title="src/app/contact/contact.component.css" path="ngmodule/src/app/contact/contact.component.css">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts"> <code-pane title="src/app/contact/contact.service.ts" path="ngmodule/src/app/contact/contact.service.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts"> <code-pane title="src/app/contact/awesome.pipe.ts" path="ngmodule/src/app/contact/awesome.pipe.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts"> <code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
In the middle of the component template, In the middle of the component template,
@ -472,7 +443,6 @@ form features such as validation aren't yet available.
Add the `FormsModule` to the `AppModule` metadata's `imports` list. Add the `FormsModule` to the `AppModule` metadata's `imports` list.
<code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -502,7 +472,6 @@ Components, directives, and pipes belong to _one module only_.
The application won't compile until you declare the contact component, directive, and pipe. The application won't compile until you declare the contact component, directive, and pipe.
Update the `declarations` in the `AppModule` accordingly: Update the `declarations` in the `AppModule` accordingly:
<code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false"> <code-example path="ngmodule/src/app/app.module.1.ts" region="declarations" linenums="false">
</code-example> </code-example>
@ -518,7 +487,6 @@ There are two directives with the same name, both called `HighlightDirective`.
To work around this, create an alias for the contact version using the `as` JavaScript import keyword. To work around this, create an alias for the contact version using the `as` JavaScript import keyword.
<code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false"> <code-example path="ngmodule/src/app/app.module.1b.ts" region="import-alias" linenums="false">
</code-example> </code-example>
@ -541,7 +509,6 @@ You want to share this service with other contact-related components that you'll
In this app, add `ContactService` to the `AppModule` metadata's `providers` list: In this app, add `ContactService` to the `AppModule` metadata's `providers` list:
<code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false"> <code-example path="ngmodule/src/app/app.module.1b.ts" region="providers" linenums="false">
</code-example> </code-example>
@ -587,72 +554,50 @@ The app file structure looks like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
title.component.(html|ts) title.component.(html|ts)
</aio-file> </aio-file>
<aio-file> <aio-file>
user.service.ts user.service.ts
</aio-file> </aio-file>
<aio-folder> <aio-folder>
contact contact
<aio-file> <aio-file>
awesome.pipe.ts awesome.pipe.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
contact.component.(css|html|ts) contact.component.(css|html|ts)
</aio-file> </aio-file>
<aio-file> <aio-file>
contact.service.ts contact.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
Try the example: Try the example:
@ -671,21 +616,16 @@ you already had a `HighlightDirective` class at the application level.
The selectors of the two directives both highlight the attached element with a different color. The selectors of the two directives both highlight the attached element with a different color.
<code-tabs> <code-tabs>
<code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts"> <code-pane title="src/app/highlight.directive.ts" path="ngmodule/src/app/highlight.directive.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts"> <code-pane title="src/app/contact/highlight.directive.ts" path="ngmodule/src/app/contact/highlight.directive.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Both directives are declared in this module so both directives are active. Both directives are declared in this module so both directives are active.
@ -782,7 +722,6 @@ It's easy to refactor the contact material into a contact feature module.
Here's the new `ContactModule`: Here's the new `ContactModule`:
<code-example path="ngmodule/src/app/contact/contact.module.2.ts"> <code-example path="ngmodule/src/app/contact/contact.module.2.ts">
</code-example> </code-example>
@ -825,21 +764,16 @@ Then import the `ContactModule` so the app can continue to display the exported
Here's the refactored version of the `AppModule` along with the previous version. Here's the refactored version of the `AppModule` along with the previous version.
<code-tabs> <code-tabs>
<code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts"> <code-pane title="src/app/app.module.ts (v2)" path="ngmodule/src/app/app.module.2.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts"> <code-pane title="src/app/app.module.ts (v1)" path="ngmodule/src/app/app.module.1b.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### ImprovementsThere's a lot to like in the revised `AppModule`. ### ImprovementsThere's a lot to like in the revised `AppModule`.
@ -885,7 +819,6 @@ Some facets of the current application merit discussion are as follows:
The new `AppComponent` template has The new `AppComponent` template has
a title, three links, and a `<router-outlet>`. a title, three links, and a `<router-outlet>`.
<code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false"> <code-example path="ngmodule/src/app/app.component.3.ts" region="template" linenums="false">
</code-example> </code-example>
@ -894,7 +827,6 @@ The `<app-contact>` element is gone; you're routing to the _Contact_ page now.
The `AppModule` has changed modestly: The `AppModule` has changed modestly:
<code-example path="ngmodule/src/app/app.module.3.ts"> <code-example path="ngmodule/src/app/app.module.3.ts">
</code-example> </code-example>
@ -922,7 +854,6 @@ that handles the app's routing concerns.
### App routing ### App routing
<code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" linenums="false">
</code-example> </code-example>
@ -942,7 +873,6 @@ You'll get to that file in a moment.
The remaining two routes use lazy loading syntax to tell the router where to find the modules: The remaining two routes use lazy loading syntax to tell the router where to find the modules:
<code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" region="lazy-routes" linenums="false">
</code-example> </code-example>
@ -963,7 +893,6 @@ the latter separated from the former by a `#`.
The `forRoot` static class method of the `RouterModule` with the provided configuration and The `forRoot` static class method of the `RouterModule` with the provided configuration and
added to the `imports` array provides the routing concerns for the module. added to the `imports` array provides the routing concerns for the module.
<code-example path="ngmodule/src/app/app-routing.module.ts" region="forRoot" linenums="false"> <code-example path="ngmodule/src/app/app-routing.module.ts" region="forRoot" linenums="false">
</code-example> </code-example>
@ -983,7 +912,6 @@ Never call `RouterModule.forRoot` in a feature-routing module.
Back in the root `AppModule`, add the `AppRoutingModule` to its `imports` list, Back in the root `AppModule`, add the `AppRoutingModule` to its `imports` list,
and the app is ready to navigate. and the app is ready to navigate.
<code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false"> <code-example path="ngmodule/src/app/app.module.3.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -992,7 +920,6 @@ and the app is ready to navigate.
The `src/app/contact` folder holds a new file, `contact-routing.module.ts`. The `src/app/contact` folder holds a new file, `contact-routing.module.ts`.
It defines the `contact` route mentioned earlier and provides a `ContactRoutingModule` as follows: It defines the `contact` route mentioned earlier and provides a `ContactRoutingModule` as follows:
<code-example path="ngmodule/src/app/contact/contact-routing.module.ts" region="routing" linenums="false"> <code-example path="ngmodule/src/app/contact/contact-routing.module.ts" region="routing" linenums="false">
</code-example> </code-example>
@ -1024,21 +951,16 @@ that has both shared [declarables](cookbook/ngmodule-faq) and services.
`ContactModule` has changed in two small but important ways. `ContactModule` has changed in two small but important ways.
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.module.3.ts" path="ngmodule/src/app/contact/contact.module.3.ts" region="class"> <code-pane title="src/app/contact/contact.module.3.ts" path="ngmodule/src/app/contact/contact.module.3.ts" region="class">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class"> <code-pane title="src/app/contact/contact.module.2.ts" path="ngmodule/src/app/contact/contact.module.2.ts" region="class">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
* It imports the `ContactRoutingModule` object from `contact-routing.module.ts`. * It imports the `ContactRoutingModule` object from `contact-routing.module.ts`.
@ -1062,54 +984,38 @@ a more interesting and useful example. Its file structure is as follows:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
hero hero
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-list.component.ts hero-list.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.component.ts hero.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.module.ts hero.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-routing.module.ts hero-routing.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
highlight.directive.ts highlight.directive.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
This is the child routing scenario familiar to readers of the This is the child routing scenario familiar to readers of the
@ -1125,7 +1031,6 @@ In the next section, [Shared modules](guide/ngmodule#shared-module "Shared modul
The `HeroModule` is a feature module like any other. The `HeroModule` is a feature module like any other.
<code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false"> <code-example path="ngmodule/src/app/hero/hero.module.3.ts" region="class" linenums="false">
</code-example> </code-example>
@ -1157,7 +1062,6 @@ and share them with the modules that need them.
Here is the `SharedModule`: Here is the `SharedModule`:
<code-example path="ngmodule/src/app/shared/shared.module.ts"> <code-example path="ngmodule/src/app/shared/shared.module.ts">
</code-example> </code-example>
@ -1237,7 +1141,6 @@ Perform the following steps:
Most of this work is familiar. The interesting part is the `CoreModule`. Most of this work is familiar. The interesting part is the `CoreModule`.
<code-example path="ngmodule/src/app/core/core.module.ts" region="v4"> <code-example path="ngmodule/src/app/core/core.module.ts" region="v4">
</code-example> </code-example>
@ -1299,21 +1202,16 @@ Having refactored to a `CoreModule` and a `SharedModule`, it's time to clean up
Here is the updated `AppModule` paired with version 3 for comparison: Here is the updated `AppModule` paired with version 3 for comparison:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4"> <code-pane title="src/app/app.module.ts (v4)" path="ngmodule/src/app/app.module.ts" region="v4">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts"> <code-pane title="src/app/app.module.ts (v3)" path="ngmodule/src/app/app.module.3.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
`AppModule` now has the following qualities: `AppModule` now has the following qualities:
@ -1325,21 +1223,16 @@ Here is the updated `AppModule` paired with version 3 for comparison:
### A trimmer _ContactModule_ ### A trimmer _ContactModule_
Here is the new `ContactModule` paired with the prior version: Here is the new `ContactModule` paired with the prior version:
<code-tabs> <code-tabs>
<code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts"> <code-pane title="src/app/contact/contact.module.ts (v4)" path="ngmodule/src/app/contact/contact.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts"> <code-pane title="src/app/contact/contact.module.ts (v3)" path="ngmodule/src/app/contact/contact.module.3.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Notice the following: Notice the following:
@ -1382,21 +1275,18 @@ Add a `CoreModule.forRoot` method that configures the core `UserService`.
You've extended the core `UserService` with an optional, injected `UserServiceConfig`. You've extended the core `UserService` with an optional, injected `UserServiceConfig`.
If a `UserServiceConfig` exists, the `UserService` sets the user name from that config. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
<code-example path="ngmodule/src/app/core/user.service.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/user.service.ts" region="ctor" linenums="false">
</code-example> </code-example>
Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object: Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object:
<code-example path="ngmodule/src/app/core/core.module.ts" region="for-root" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="for-root" linenums="false">
</code-example> </code-example>
Lastly, call it within the `imports` list of the `AppModule`. Lastly, call it within the `imports` list of the `AppModule`.
<code-example path="ngmodule/src/app/app.module.ts" region="import-for-root" linenums="false"> <code-example path="ngmodule/src/app/app.module.ts" region="import-for-root" linenums="false">
</code-example> </code-example>
@ -1435,7 +1325,6 @@ It looks like it is supposed to go to a specific question/section within the pag
You could hope that no developer makes that mistake. You could hope that no developer makes that mistake.
Or you can guard against it and fail fast by adding the following `CoreModule` constructor. Or you can guard against it and fail fast by adding the following `CoreModule` constructor.
<code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false"> <code-example path="ngmodule/src/app/core/core.module.ts" region="ctor" linenums="false">
</code-example> </code-example>

View File

@ -31,7 +31,6 @@ In this page, you'll use pipes to transform a component's birthday property into
a human-friendly date. a human-friendly date.
<code-example path="pipes/src/app/hero-birthday1.component.ts" linenums="false"> <code-example path="pipes/src/app/hero-birthday1.component.ts" linenums="false">
</code-example> </code-example>
@ -39,7 +38,6 @@ a human-friendly date.
Focus on the component's template. Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" linenums="false">
</code-example> </code-example>
@ -92,7 +90,6 @@ Modify the birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**: After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
<code-example path="pipes/src/app/app.component.html" region="format-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="format-birthday" linenums="false">
</code-example> </code-example>
@ -107,7 +104,6 @@ Write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component: to the component's `format` property. Here's the template for that component:
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" linenums="false"> <code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -117,7 +113,6 @@ That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`). (`'shortDate'`) and a longer form (`'fullDate'`).
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" linenums="false"> <code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -149,7 +144,6 @@ the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
The birthday displays as **<samp>APR 15, 1988</samp>**. The birthday displays as **<samp>APR 15, 1988</samp>**.
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="chained-birthday" linenums="false">
</code-example> </code-example>
@ -158,7 +152,6 @@ This example&mdash;which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**&mdash;
the same pipes as above, but passes in a parameter to `date` as well. the same pipes as above, but passes in a parameter to `date` as well.
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" linenums="false"> <code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" linenums="false">
</code-example> </code-example>
@ -170,7 +163,6 @@ You can write your own custom pipes.
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers: Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
<code-example path="pipes/src/app/exponential-strength.pipe.ts" linenums="false"> <code-example path="pipes/src/app/exponential-strength.pipe.ts" linenums="false">
</code-example> </code-example>
@ -183,8 +175,8 @@ accepts an input value followed by optional parameters and returns the transform
* There will be one additional argument to the `transform` method for each parameter passed to the pipe. * There will be one additional argument to the `transform` method for each parameter passed to the pipe.
Your pipe has one such parameter: the `exponent`. Your pipe has one such parameter: the `exponent`.
* To tell Angular that this is a pipe, you apply the * To tell Angular that this is a pipe, you apply the
`@Pipe` #{_decorator}, which you import from the core Angular library. `@Pipe` decorator, which you import from the core Angular library.
* The `@Pipe` #{_decorator} allows you to define the * The `@Pipe` decorator allows you to define the
pipe name that you'll use within template expressions. It must be a valid JavaScript identifier. pipe name that you'll use within template expressions. It must be a valid JavaScript identifier.
Your pipe's name is `exponentialStrength`. Your pipe's name is `exponentialStrength`.
@ -202,7 +194,6 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
Now you need a component to demonstrate the pipe. Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" linenums="false"> <code-example path="pipes/src/app/power-booster.component.ts" linenums="false">
</code-example> </code-example>
@ -216,7 +207,7 @@ Now you need a component to demonstrate the pipe.
Note the following: Note the following:
* You use your custom pipe the same way you use built-in pipes. * You use your custom pipe the same way you use built-in pipes.
* You must include your pipe in the `!{_decls}` #{_array} of the `!{_appMod}`. * You must include your pipe in the `declarations` array of the `AppModule`.
~~~ {.callout.is-helpful} ~~~ {.callout.is-helpful}
@ -224,7 +215,7 @@ Note the following:
<header> <header>
Remember the Remember the declarations array
</header> </header>
You must manually register custom pipes. You must manually register custom pipes.
@ -245,7 +236,6 @@ Upgrade the example to a "Power Boost Calculator" that combines
your pipe and two-way data binding with `ngModel`. your pipe and two-way data binding with `ngModel`.
<code-example path="pipes/src/app/power-boost-calculator.component.ts"> <code-example path="pipes/src/app/power-boost-calculator.component.ts">
</code-example> </code-example>
@ -271,37 +261,33 @@ Angular picks a simpler, faster change detection algorithm when you use a pipe.
### No pipe ### No pipe
In the next example, the component uses the default, aggressive change detection strategy to monitor and update In the next example, the component uses the default, aggressive change detection strategy to monitor and update
its display of every hero in the `heroes` #{_array}. Here's the template: its display of every hero in the `heroes` array. Here's the template:
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" linenums="false">
</code-example> </code-example>
The companion component class provides heroes, adds heroes into the #{_array}, and can reset the #{_array}. The companion component class provides heroes, adds heroes into the array, and can reset the array.
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" linenums="false">
</code-example> </code-example>
You can add heroes and Angular updates the display when you do. You can add heroes and Angular updates the display when you do.
If you click the `reset` button, Angular replaces `heroes` with a new #{_array} of the original heroes and updates the display. If you click the `reset` button, Angular replaces `heroes` with a new array of the original heroes and updates the display.
If you added the ability to remove or change a hero, Angular would detect those changes and update the display as well. If you added the ability to remove or change a hero, Angular would detect those changes and update the display as well.
### Flying-heroes pipe ### Flying-heroes pipe
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly. Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" linenums="false">
</code-example> </code-example>
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier. Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" linenums="false"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" linenums="false">
</code-example> </code-example>
@ -314,20 +300,19 @@ It's just using a different change-detection algorithm that ignores changes to t
Notice how a hero is added: Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false"> <code-example path="pipes/src/app/flying-heroes.component.ts" region="push" linenums="false">
</code-example> </code-example>
You add the hero into the `heroes` #{_array}. The reference to the #{_array} hasn't changed. You add the hero into the `heroes` array. The reference to the array hasn't changed.
It's the same #{_array}. That's all Angular cares about. From its perspective, *same #{_array}, no change, no display update*. It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
To fix that, create an #{_array} with the new hero appended and assign that to `heroes`. To fix that, create an array with the new hero appended and assign that to `heroes`.
This time Angular detects that the #{_array} reference has changed. This time Angular detects that the array reference has changed.
It executes the pipe and updates the display with the new #{_array}, which includes the new flying hero. It executes the pipe and updates the display with the new array, which includes the new flying hero.
If you *mutate* the #{_array}, no pipe is invoked and the display isn't updated; If you *mutate* the array, no pipe is invoked and the display isn't updated;
if you *replace* the #{_array}, the pipe executes and the display is updated. if you *replace* the array, the pipe executes and the display is updated.
The Flying Heroes application extends the The Flying Heroes application extends the
code with checkbox switches and additional displays to help you experience these effects. code with checkbox switches and additional displays to help you experience these effects.
@ -336,8 +321,8 @@ code with checkbox switches and additional displays to help you experience these
<img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img> <img src='assets/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"> </img>
</figure> </figure>
Replacing the #{_array} is an efficient way to signal Angular to update the display. Replacing the array is an efficient way to signal Angular to update the display.
When do you replace the #{_array}? When the data change. When do you replace the array? When the data change.
That's an easy rule to follow in *this* example That's an easy rule to follow in *this* example
where the only way to change the data is by adding a hero. where the only way to change the data is by adding a hero.
@ -359,7 +344,6 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
impure like this: impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" linenums="false">
</code-example> </code-example>
@ -373,7 +357,7 @@ A pure change is either a change to a primitive input value (`String`, `Number`,
or a changed object reference (`Date`, `Array`, `Function`, `Object`). or a changed object reference (`Date`, `Array`, `Function`, `Object`).
Angular ignores changes within (composite) objects. Angular ignores changes within (composite) objects.
It won't call a pure pipe if you change an input month, add to an input #{_array}, or update an input object property. It won't call a pure pipe if you change an input month, add to an input array, or update an input object property.
This may seem restrictive but it's also fast. This may seem restrictive but it's also fast.
An object reference check is fast&mdash;much faster than a deep check for An object reference check is fast&mdash;much faster than a deep check for
@ -407,21 +391,16 @@ An expensive, long-running pipe could destroy the user experience.
A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`. A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
The complete implementation is as follows: The complete implementation is as follows:
<code-tabs> <code-tabs>
<code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure"> <code-pane title="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure">
</code-pane> </code-pane>
<code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure"> <code-pane title="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
You inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally. You inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
@ -430,7 +409,6 @@ The only difference is the `pure` flag in the pipe metadata.
This is a good candidate for an impure pipe because the `transform` function is trivial and fast. This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" title="src/app/flying-heroes.pipe.ts (filter)" region="filter"> <code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" title="src/app/flying-heroes.pipe.ts (filter)" region="filter">
</code-example> </code-example>
@ -438,14 +416,13 @@ This is a good candidate for an impure pipe because the `transform` function is
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`. You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
<code-example path="pipes/src/app/flying-heroes-impure.component.html" linenums="false" title="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes">
<code-example path="pipes/src/app/flying-heroes" linenums="false" title="src/app/flying-heroes (_region)" region="_region">
</code-example> </code-example>
The only substantive change is the pipe in the template. The only substantive change is the pipe in the template.
You can confirm in the <live-example></live-example> that the _flying heroes_ You can confirm in the <live-example></live-example> that the _flying heroes_
display updates as you add heroes, even when you mutate the `heroes` #{_array}. display updates as you add heroes, even when you mutate the `heroes` array.
<h3 id='async-pipe'> <h3 id='async-pipe'>
@ -453,16 +430,15 @@ display updates as you add heroes, even when you mutate the `heroes` #{_array}.
</h3> </h3>
The Angular `AsyncPipe` is an interesting example of an impure pipe. The Angular `AsyncPipe` is an interesting example of an impure pipe.
The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input The `AsyncPipe` accepts a `Promise` or `Observable` as input
and subscribes to the input automatically, eventually returning the emitted values. and subscribes to the input automatically, eventually returning the emitted values.
The `AsyncPipe` is also stateful. The `AsyncPipe` is also stateful.
The pipe maintains a subscription to the input `#{_Observable}` and The pipe maintains a subscription to the input `Observable` and
keeps delivering values from that `#{_Observable}` as they arrive. keeps delivering values from that `Observable` as they arrive.
This next example binds an `#{_Observable}` of message strings
(`message#{_dollar}`) to a view with the `async` pipe.
This next example binds an `Observable` of message strings
(`message$`) to a view with the `async` pipe.
<code-example path="pipes/src/app/hero-async-message.component.ts"> <code-example path="pipes/src/app/hero-async-message.component.ts">
@ -483,8 +459,7 @@ Remember that impure pipes are called every few milliseconds.
If you're not careful, this pipe will punish the server with requests. If you're not careful, this pipe will punish the server with requests.
In the following code, the pipe only calls the server when the request URL changes and it caches the server response. In the following code, the pipe only calls the server when the request URL changes and it caches the server response.
The code<span if-docs="ts"> uses the [Angular http](guide/server-communication) client to retrieve data</span>: The code uses the [Angular http](guide/server-communication) client to retrieve data</span>:
<code-example path="pipes/src/app/fetch-json.pipe.ts"> <code-example path="pipes/src/app/fetch-json.pipe.ts">
@ -495,7 +470,6 @@ Now demonstrate it in a harness component whose template defines two bindings to
both requesting the heroes from the `heroes.json` file. both requesting the heroes from the `heroes.json` file.
<code-example path="pipes/src/app/hero-list.component.ts"> <code-example path="pipes/src/app/hero-list.component.ts">
</code-example> </code-example>

View File

@ -4,7 +4,6 @@ Angular applications are made up of _components_.
A _component_ is the combination of an HTML template and a component class that controls a portion of the screen. Here is an example of a component that displays a simple string: A _component_ is the combination of an HTML template and a component class that controls a portion of the screen. Here is an example of a component that displays a simple string:
<code-example path="quickstart/src/app/app.component.ts" linenums="false"> <code-example path="quickstart/src/app/app.component.ts" linenums="false">
</code-example> </code-example>
@ -21,13 +20,11 @@ and prepare for development of a real Angular application.
~~~ ~~~
Every component begins with an `@Component` [!{_decorator}](guide/glossary) Every component begins with an `@Component` [decorator](guide/glossary)
<span if-docs="ts">function</span> that function that takes a _metadata_ object. The metadata object describes how the HTML template and component class work together.
<span if-docs="ts">takes a _metadata_ object. The metadata object</span> describes how the HTML template and component class work together.
The `selector` property tells Angular to display the component inside a custom `<my-app>` tag in the `index.html`. The `selector` property tells Angular to display the component inside a custom `<my-app>` tag in the `index.html`.
<code-example path="quickstart/src/index.html" region="my-app" linenums="false"> <code-example path="quickstart/src/index.html" region="my-app" linenums="false">
</code-example> </code-example>
@ -37,6 +34,29 @@ The message starts with "Hello" and ends with `{{name}}`,
which is an Angular [interpolation binding](guide/guide/displaying-data) expression. which is an Angular [interpolation binding](guide/guide/displaying-data) expression.
At runtime, Angular replaces `{{name}}` with the value of the component's `name` property. At runtime, Angular replaces `{{name}}` with the value of the component's `name` property.
Interpolation binding is one of many Angular features you'll discover in this documentation. Interpolation binding is one of many Angular features you'll discover in this documentation.
In the example, change the component class's `name` property from `'Angular'` to `'World'` and see what happens.
~~~ {.callout.is-helpful}
<header>
A word about TypeScript
</header>
<p>
This example is written in <a href="http://www.typescriptlang.org/" target="_blank" title="TypeScript">TypeScript</a>, a superset of JavaScript. Angular
uses TypeScript because its types make it easy to support developer productivity with tooling. You can also write Angular code in JavaScript; <a href="cookbook/ts-to-js.html">this guide</a> explains how.
</p>
~~~
~~~ {.l-sub-section} ~~~ {.l-sub-section}

View File

@ -159,7 +159,6 @@ You'll need a `hero` class and some hero data.
Create a new `data-model.ts` file in the `app` directory and copy the content below into it. Create a new `data-model.ts` file in the `app` directory and copy the content below into it.
<code-example path="reactive-forms/src/app/data-model.ts" linenums="false"> <code-example path="reactive-forms/src/app/data-model.ts" linenums="false">
</code-example> </code-example>
@ -176,7 +175,6 @@ Make a new file called
`hero-detail.component.ts` in the `app` directory and import these symbols: `hero-detail.component.ts` in the `app` directory and import these symbols:
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -184,7 +182,6 @@ Make a new file called
Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata: Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata:
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" linenums="false">
</code-example> </code-example>
@ -195,7 +192,6 @@ a `FormControl` instance directly.
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -227,7 +223,6 @@ read the [Form Validation](cookbook/form-validation) cookbook.
Now create the component's template, `src/app/hero-detail.component.html`, with the following markup. Now create the component's template, `src/app/hero-detail.component.html`, with the following markup.
<code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" linenums="false">
</code-example> </code-example>
@ -264,7 +259,6 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
1. Add `HeroDetailComponent` to the declarations array. 1. Add `HeroDetailComponent` to the declarations array.
<code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false"> <code-example path="reactive-forms/src/app/app.module.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -276,7 +270,6 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
## Display the _HeroDetailComponent_ ## Display the _HeroDetailComponent_
Revise the `AppComponent` template so it displays the `HeroDetailComponent`. Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
<code-example path="reactive-forms/src/app/app.component.1.ts" linenums="false"> <code-example path="reactive-forms/src/app/app.component.1.ts" linenums="false">
</code-example> </code-example>
@ -311,7 +304,6 @@ You used bootstrap CSS classes in the template HTML of both the `AppComponent` a
Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`: Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
<code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false"> <code-example path="reactive-forms/src/index.html" region="bootstrap" linenums="false">
</code-example> </code-example>
@ -333,7 +325,6 @@ This is simple to do. To add a `FormGroup`, add it to the imports section
of `hero-detail.component.ts`: of `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -341,7 +332,6 @@ of `hero-detail.component.ts`:
In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows: In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" linenums="false">
</code-example> </code-example>
@ -350,7 +340,6 @@ Now that you've made changes in the class, they need to be reflected in the
template. Update `hero-detail.component.html` by replacing it with the following. template. Update `hero-detail.component.html` by replacing it with the following.
<code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" linenums="false">
</code-example> </code-example>
@ -402,7 +391,6 @@ To see the form model, add the following line after the
closing `form` tag in the `hero-detail.component.html`: closing `form` tag in the `hero-detail.component.html`:
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example> </code-example>
@ -435,7 +423,6 @@ clutter by handling details of control creation for you.
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`: To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -450,7 +437,6 @@ by following this plan:
The revised `HeroDetailComponent` looks like this: The revised `HeroDetailComponent` looks like this:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" linenums="false">
</code-example> </code-example>
@ -470,7 +456,6 @@ demonstrates the simplicity of using `Validators.required` in reactive forms.
First, import the `Validators` symbol. First, import the `Validators` symbol.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -481,7 +466,6 @@ The first item is the initial value for `name`;
the second is the required validator, `Validators.required`. the second is the required validator, `Validators.required`.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" linenums="false">
</code-example> </code-example>
@ -498,7 +482,6 @@ Configuring validation is harder in template-driven forms where you must wrap va
Update the diagnostic message at the bottom of the template to display the form's validity status. Update the diagnostic message at the bottom of the template to display the form's validity status.
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" linenums="false">
</code-example> </code-example>
@ -526,7 +509,6 @@ A hero has an address, a super power and sometimes a sidekick too.
The address has a state property. The user will select a state with a `<select>` box and you'll populate The address has a state property. The user will select a state with a `<select>` box and you'll populate
the `<option>` elements with states. So import `states` from `data-model.ts`. the `<option>` elements with states. So import `states` from `data-model.ts`.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -534,7 +516,6 @@ the `<option>` elements with states. So import `states` from `data-model.ts`.
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows. Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" linenums="false">
</code-example> </code-example>
@ -543,7 +524,6 @@ Then add corresponding markup in `hero-detail.component.html`
within the `form` element. within the `form` element.
<code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-4.component.html" linenums="false">
</code-example> </code-example>
@ -595,7 +575,6 @@ Let that be the parent `FormGroup`.
Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls; Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls;
assign the result to a new `address` property of the parent `FormGroup`. assign the result to a new `address` property of the parent `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" linenums="false">
</code-example> </code-example>
@ -611,7 +590,6 @@ To make this change visually obvious, slip in an `<h4>` header near the top with
The new _address_ HTML looks like this: The new _address_ HTML looks like this:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" linenums="false">
</code-example> </code-example>
@ -640,7 +618,6 @@ page by adding the following to the template,
immediately after the `{{form.value | json}}` interpolation as follows: immediately after the `{{form.value | json}}` interpolation as follows:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" linenums="false">
</code-example> </code-example>
@ -648,7 +625,6 @@ immediately after the `{{form.value | json}}` interpolation as follows:
To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control. To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control.
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" linenums="false">
</code-example> </code-example>
@ -664,112 +640,78 @@ such as one of the following:
<table width="100%"> <table width="100%">
<col width="10%"> <col width="10%">
</col> </col>
<col width="90%"> <col width="90%">
</col> </col>
<tr> <tr>
<th> <th>
Property Property
</th> </th>
<th> <th>
Description Description
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.value</code> <code>myControl.value</code>
</td> </td>
<td> <td>
the value of a `FormControl`. the value of a `FormControl`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.status</code> <code>myControl.status</code>
</td> </td>
<td> <td>
the validity of a `FormControl`. Possible values: `VALID`, the validity of a `FormControl`. Possible values: `VALID`,
`INVALID`, `PENDING`, or `DISABLED`. `INVALID`, `PENDING`, or `DISABLED`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.pristine</code> <code>myControl.pristine</code>
</td> </td>
<td> <td>
`true` if the user has _not_ changed the value in the UI. `true` if the user has _not_ changed the value in the UI.
Its opposite is `myControl.dirty`. Its opposite is `myControl.dirty`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>myControl.untouched</code> <code>myControl.untouched</code>
</td> </td>
<td> <td>
`true` if the control user has not yet entered the HTML control `true` if the control user has not yet entered the HTML control
and triggered its blur event. Its opposite is `myControl.touched`. and triggered its blur event. Its opposite is `myControl.touched`.
</td> </td>
</tr> </tr>
</table> </table>
Learn about other `FormControl` properties in the Learn about other `FormControl` properties in the
@ -811,7 +753,6 @@ In this `HeroDetailComponent`, the two models are quite close.
Recall the definition of `Hero` in `data-model.ts`: Recall the definition of `Hero` in `data-model.ts`:
<code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false"> <code-example path="reactive-forms/src/app/data-model.ts" region="model-classes" linenums="false">
</code-example> </code-example>
@ -819,7 +760,6 @@ Recall the definition of `Hero` in `data-model.ts`:
Here, again, is the component's `FormGroup` definition. Here, again, is the component's `FormGroup` definition.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" linenums="false">
</code-example> </code-example>
@ -835,14 +775,12 @@ Nonetheless, the two models are pretty close in shape and you'll see in a moment
to the _form model_ with the `patchValue` and `setValue` methods. to the _form model_ with the `patchValue` and `setValue` methods.
Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows: Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example> </code-example>
Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes: Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" linenums="false">
</code-example> </code-example>
@ -861,7 +799,6 @@ With **`setValue`**, you assign _every_ form control value _at once_
by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`. by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" linenums="false">
</code-example> </code-example>
@ -882,7 +819,6 @@ because its shape is similar to the component's `FormGroup` structure.
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all. You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all.
This explains the conditional setting of the `address` property in the data object argument: This explains the conditional setting of the `address` property in the data object argument:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" linenums="false">
</code-example> </code-example>
@ -893,7 +829,6 @@ by supplying an object of key/value pairs for just the controls of interest.
This example sets only the form's `name` control. This example sets only the form's `name` control.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" linenums="false">
</code-example> </code-example>
@ -913,7 +848,6 @@ When the user clicks on a hero, the list component passes the selected hero into
by binding to its `hero` input property. by binding to its `hero` input property.
<code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false"> <code-example path="reactive-forms/src/app/hero-list.component.1.html" linenums="false">
</code-example> </code-example>
@ -927,14 +861,12 @@ as the following steps demonstrate.
First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`. First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" linenums="false">
</code-example> </code-example>
Add the `hero` input property. Add the `hero` input property.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" linenums="false">
</code-example> </code-example>
@ -942,7 +874,6 @@ Add the `hero` input property.
Add the `ngOnChanges` method to the class as follows: Add the `ngOnChanges` method to the class as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" linenums="false">
</code-example> </code-example>
@ -954,7 +885,6 @@ control values from the previous hero are cleared and
status flags are restored to the _pristine_ state. status flags are restored to the _pristine_ state.
You could call `reset` at the top of `ngOnChanges` like this. You could call `reset` at the top of `ngOnChanges` like this.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" linenums="false">
</code-example> </code-example>
@ -963,7 +893,6 @@ The `reset` method has an optional `state` value so you can reset the flags _and
Internally, `reset` passes the argument to `setValue`. Internally, `reset` passes the argument to `setValue`.
A little refactoring and `ngOnChanges` becomes this: A little refactoring and `ngOnChanges` becomes this:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" linenums="false">
</code-example> </code-example>
@ -1023,7 +952,6 @@ An Angular `FormArray` can display an array of _address_ `FormGroups`.
To get access to the `FormArray` class, import it into `hero-detail.component.ts`: To get access to the `FormArray` class, import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" linenums="false">
</code-example> </code-example>
@ -1039,7 +967,6 @@ let the user add or modify addresses (removing addresses is your homework).
Youll need to redefine the form model in the `HeroDetailComponent` constructor, Youll need to redefine the form model in the `HeroDetailComponent` constructor,
which currently only displays the first hero address in an _address_ `FormGroup`. which currently only displays the first hero address in an _address_ `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" linenums="false">
</code-example> </code-example>
@ -1050,7 +977,6 @@ From the user's point of view, heroes don't have _addresses_.
_Addresses_ are for mere mortals. Heroes have _secret lairs_! _Addresses_ are for mere mortals. Heroes have _secret lairs_!
Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition: Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" linenums="false">
</code-example> </code-example>
@ -1081,7 +1007,6 @@ the parent `HeroListComponent` sets the `HeroListComponent.hero` input property
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`, The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
initialized by an array of hero address `FormGroups`. initialized by an array of hero address `FormGroups`.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" linenums="false">
</code-example> </code-example>
@ -1097,7 +1022,6 @@ The `HeroDetailComponent` should be able to display, add, and remove items from
Use the `FormGroup.get` method to acquire a reference to that `FormArray`. Use the `FormGroup.get` method to acquire a reference to that `FormArray`.
Wrap the expression in a `secretLairs` convenience property for clarity and re-use. Wrap the expression in a `secretLairs` convenience property for clarity and re-use.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" linenums="false">
</code-example> </code-example>
@ -1124,14 +1048,12 @@ You'll re-use that index to compose a unique label for each address.
Here's the skeleton for the _secret lairs_ section of the HTML template: Here's the skeleton for the _secret lairs_ section of the HTML template:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" linenums="false">
</code-example> </code-example>
Here's the complete template for the _secret lairs_ section: Here's the complete template for the _secret lairs_ section:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array">
</code-example> </code-example>
@ -1140,7 +1062,6 @@ Here's the complete template for the _secret lairs_ section:
Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it. Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" linenums="false">
</code-example> </code-example>
@ -1148,7 +1069,6 @@ Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a ne
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method. Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method.
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" linenums="false">
</code-example> </code-example>
@ -1202,14 +1122,12 @@ You don't need to know much about RxJS `Observable` to monitor form control valu
Add the following method to log changes to the value of the _name_ `FormControl`. Add the following method to log changes to the value of the _name_ `FormControl`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" linenums="false">
</code-example> </code-example>
Call it in the constructor, after creating the form. Call it in the constructor, after creating the form.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" linenums="false">
</code-example> </code-example>
@ -1217,7 +1135,6 @@ Call it in the constructor, after creating the form.
The `logNameChange` method pushes name-change values into a `nameChangeLog` array. The `logNameChange` method pushes name-change values into a `nameChangeLog` array.
Display that array at the bottom of the component template with this `*ngFor` binding: Display that array at the bottom of the component template with this `*ngFor` binding:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" linenums="false">
</code-example> </code-example>
@ -1251,7 +1168,6 @@ In this sample application, when the user submits the form,
the `HeroDetailComponent` will pass an instance of the hero _data model_ the `HeroDetailComponent` will pass an instance of the hero _data model_
to a save method on the injected `HeroService`. to a save method on the injected `HeroService`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" linenums="false">
</code-example> </code-example>
@ -1261,7 +1177,6 @@ So you create a new `hero` from a combination of original hero values (the `hero
and deep copies of the changed form model values, using the `prepareSaveHero` helper. and deep copies of the changed form model values, using the `prepareSaveHero` helper.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" linenums="false">
</code-example> </code-example>
@ -1287,7 +1202,6 @@ The user cancels changes and reverts the form to the original state by pressing
Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_. Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" linenums="false">
</code-example> </code-example>
@ -1295,7 +1209,6 @@ Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _fo
### Buttons ### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template: Add the "Save" and "Revert" buttons near the top of the component's template:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" linenums="false"> <code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" linenums="false">
</code-example> </code-example>
@ -1328,57 +1241,40 @@ This page covered:
The key files of the final version are as follows: The key files of the final version are as follows:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="reactive-forms/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts"> <code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html"> <code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html"> <code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts"> <code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts"> <code-pane title="src/app/data-model.ts" path="reactive-forms/src/app/data-model.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts"> <code-pane title="src/app/hero.service.ts" path="reactive-forms/src/app/hero.service.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
You can download the complete source for all steps in this guide You can download the complete source for all steps in this guide

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,15 @@ scripting attacks. It doesn't cover application-level security, such as authenti
this user?_) and authorization (_What can this user do?_). this user?_) and authorization (_What can this user do?_).
For more information about the attacks and mitigations described below, see [OWASP Guide Project](https://www.owasp.org/index.php/Category:OWASP_Guide_Project). For more information about the attacks and mitigations described below, see [OWASP Guide Project](https://www.owasp.org/index.php/Category:OWASP_Guide_Project).
# Contents
* [Reporting vulnerabilities](guide/security#report-issues).
* [Best practices](guide/security#best-practices).
* [Preventing cross-site scripting (XSS)](guide/security#xss).
* [Trusting safe values](guide/security#bypass-security-apis).
* [HTTP-Level vulnerabilities](guide/security#http).
* [Auditing Angular applications](guide/security#code-review).
You can run the <live-example></live-example> in Plunker and download the code from there. You can run the <live-example></live-example> in Plunker and download the code from there.
@ -97,7 +106,6 @@ The following template binds the value of `htmlSnippet`, once by interpolating i
content, and once by binding it to the `innerHTML` property of an element: content, and once by binding it to the `innerHTML` property of an element:
<code-example path="security/src/app/inner-html-binding.component.html"> <code-example path="security/src/app/inner-html-binding.component.html">
</code-example> </code-example>
@ -110,12 +118,10 @@ a value that an attacker might control into `innerHTML` normally causes an XSS
vulnerability. For example, code contained in a `<script>` tag is executed: vulnerability. For example, code contained in a `<script>` tag is executed:
<code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" title="src/app/inner-html-binding.component.ts (class)" region="class"> <code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" title="src/app/inner-html-binding.component.ts (class)" region="class">
</code-example> </code-example>
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>` Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>`
tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element. tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element.
@ -160,7 +166,6 @@ carries a high risk of introducing template-injection vulnerabilities.
<h2 id='bypass-security-apis'> <h2 id='bypass-security-apis'>
Trusting safe values Trusting safe values
</h2> </h2>
@ -186,7 +191,6 @@ your intended use of the value. Imagine that the following template needs to bin
`javascript:alert(...)` call: `javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL"> <code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (URL)" region="URL">
</code-example> </code-example>
@ -196,7 +200,6 @@ in development mode, logs this action to the console. To prevent
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call: this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url"> <code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example> </code-example>
@ -215,14 +218,12 @@ could execute. So call a method on the controller to construct a trusted video U
Angular to allow binding into `<iframe src>`: Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe"> <code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example> </code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"> <code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example> </code-example>
@ -230,7 +231,6 @@ Angular to allow binding into `<iframe src>`:
<h2 id='http'> <h2 id='http'>
HTTP-level vulnerabilities HTTP-level vulnerabilities
</h2> </h2>

View File

@ -20,14 +20,15 @@ Modern browsers support two HTTP-based APIs:
[JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support [JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs. The Angular HTTP library simplifies application programming with the **XHR** and **JSONP** APIs.
# Contents # Contents
* [Demos](guide/server-communication#demos) * [Demos](guide/server-communication#demos)
* [Providing HTTP Services](guide/server-communication#http-providers) * [Providing HTTP Services](guide/server-communication#http-providers)
* [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client) * [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client)
- [The `HeroListComponent` class](guide/server-communication#HeroListComponent) - [The `HeroListComponent` class](guide/server-communication#HeroListComponent)
* [Fetch data with `http.get()`](guide/server-communication#fetch-data) * [Fetch data with `http.get()`](guide/server-communication#fetch-data)
<li if-docs="ts"> [RxJS library](guide/server-communication#rxjs-library) <li>[RxJS library](guide/server-communication#rxjs-library)
<ul> <ul>
<li> [Enable RxJS operators](guide/server-communication#enable-rxjs-operators)</li> <li> [Enable RxJS operators](guide/server-communication#enable-rxjs-operators)</li>
</ul> </ul>
@ -41,16 +42,16 @@ The !{_Angular_http_library} simplifies application programming with the **XHR**
- [Headers](guide/server-communication#headers) - [Headers](guide/server-communication#headers)
- [JSON results](guide/server-communication#json-results) - [JSON results](guide/server-communication#json-results)
<ul><li if-docs="ts"> [Fall back to promises](guide/server-communication#promises)</ul> <ul><li> [Fall back to promises](guide/server-communication#promises)</ul>
* [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors) * [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors)
<ul if-docs="ts"> <ul>
<li> [Search Wikipedia](guide/server-communication#search-wikipedia)</li> <li> [Search Wikipedia](guide/server-communication#search-wikipedia)</li>
<li> [Search parameters](guide/server-communication#search-parameters)</li> <li> [Search parameters](guide/server-communication#search-parameters)</li>
<li> [The WikiComponent](guide/server-communication#wikicomponent)</li> <li> [The WikiComponent](guide/server-communication#wikicomponent)</li>
</ul> </ul>
* [A wasteful app](guide/server-communication#wasteful-app) * [A wasteful app](guide/server-communication#wasteful-app)
<li if-docs="ts"> [More fun with Observables](guide/server-communication#more-observables) <li> [More fun with Observables](guide/server-communication#more-observables)
<ul> <ul>
<li> [Create a stream of search terms](guide/server-communication#create-stream)</li> <li> [Create a stream of search terms](guide/server-communication#create-stream)</li>
<li> [Listen for search terms](guide/server-communication#listen-for-search-terms)</li> <li> [Listen for search terms](guide/server-communication#listen-for-search-terms)</li>
@ -70,12 +71,11 @@ A <live-example>live example</live-example> illustrates these topics.
This page describes server communication with the help of the following demos: This page describes server communication with the help of the following demos:
- [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client). - [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client).
- [Fall back to !{_Promise}s](guide/server-communication#promises). - [Fall back to Promises](guide/server-communication#promises).
- [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors). - [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors).
- [More fun with Observables](guide/server-communication#more-observables). - [More fun with Observables](guide/server-communication#more-observables).
The root `AppComponent` orchestrates these demos: The root `AppComponent` orchestrates these demos:
<code-example path="server-communication/src/app/app.component.ts"> <code-example path="server-communication/src/app/app.component.ts">
</code-example> </code-example>
@ -85,9 +85,20 @@ The root `AppComponent` orchestrates these demos:
First, configure the application to use server communication facilities. First, configure the application to use server communication facilities.
The !{_Angular_Http} client communicates with the server using a familiar HTTP request/response protocol. The Angular <code>Http</code> client communicates with the server using a familiar HTTP request/response protocol.
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}. The `Http` client is one of a family of services in the Angular HTTP library.
Before you can use the `!{_Http}` client, you need to register it as a service provider with the dependency injection system.
~~~ {.l-sub-section}
When importing from the `@angular/http` module, SystemJS knows how to load services from
the Angular HTTP library
because the `systemjs.config.js` file maps to that module name.
~~~
Before you can use the `Http` client, you need to register it as a service provider with the dependency injection system.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
@ -100,14 +111,13 @@ Read about providers in the [Dependency Injection](guide/dependency-injection) p
Register providers by importing other NgModules to the root NgModule in `app.module.ts`. Register providers by importing other NgModules to the root NgModule in `app.module.ts`.
<code-example path="server-communication/src/app/app.module.1.ts" linenums="false"> <code-example path="server-communication/src/app/app.module.1.ts" linenums="false">
</code-example> </code-example>
Begin by importing the necessary members. Begin by importing the necessary members.
The newcomers are the `HttpModule` and the `JsonpModule` from the !{_Angular_http_library}. For more information about imports and related terminology, see the [MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) on the `import` statement. The newcomers are the `HttpModule` and the `JsonpModule` from the Angular HTTP library. For more information about imports and related terminology, see the [MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) on the `import` statement.
To add these modules to the application, pass them to the `imports` array in the root `@NgModule`. To add these modules to the application, pass them to the `imports` array in the root `@NgModule`.
@ -125,7 +135,7 @@ Loading its module now saves time.
The first demo is a mini-version of the [tutorial](tutorial)'s "Tour of Heroes" (ToH) application. The first demo is a mini-version of the [tutorial](tutorial)'s "Tour of Heroes" (ToH) application.
This version gets some heroes from the server, displays them in a list, lets the user add new heroes, and saves them to the server. This version gets some heroes from the server, displays them in a list, lets the user add new heroes, and saves them to the server.
The app uses the !{_Angular_Http} client to communicate via **XMLHttpRequest (XHR)**. The app uses the Angular <code>Http</code> client to communicate via **XMLHttpRequest (XHR)**.
It works like this: It works like this:
@ -135,7 +145,6 @@ It works like this:
This demo has a single component, the `HeroListComponent`. Here's its template: This demo has a single component, the `HeroListComponent`. Here's its template:
<code-example path="server-communication/src/app/toh/hero-list.component.html"> <code-example path="server-communication/src/app/toh/hero-list.component.html">
</code-example> </code-example>
@ -158,7 +167,6 @@ Below the button is an area for an error message.
### The *HeroListComponent* class ### The *HeroListComponent* class
Here's the component class: Here's the component class:
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="component">
</code-example> </code-example>
@ -166,7 +174,7 @@ Here's the component class:
Angular [injects](guide/dependency-injection) a `HeroService` into the constructor Angular [injects](guide/dependency-injection) a `HeroService` into the constructor
and the component calls that service to fetch and save data. and the component calls that service to fetch and save data.
The component **does not talk directly to the !{_Angular_Http} client**. The component **does not talk directly to the Angular <code>Http</code> client**.
The component doesn't know or care how it gets the data. The component doesn't know or care how it gets the data.
It delegates to the `HeroService`. It delegates to the `HeroService`.
@ -186,7 +194,7 @@ Components are easier to test and debug when their constructors are simple, and
~~~ ~~~
The service's `getHeroes()` and `create()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server. The service's `getHeroes()` and `create()` methods return an `Observable` of hero data that the Angular <code>Http</code> client fetched from the server.
Think of an `Observable` as a stream of events published by some source. Think of an `Observable` as a stream of events published by some source.
To listen for events in this stream, ***subscribe*** to the `Observable`. To listen for events in this stream, ***subscribe*** to the `Observable`.
@ -202,28 +210,24 @@ With a basic understanding of the component, you're ready to look inside the `He
In many of the previous samples the app faked the interaction with the server by In many of the previous samples the app faked the interaction with the server by
returning mock heroes in a service like this one: returning mock heroes in a service like this one:
<code-example path="toh-4/src/app/hero.service.ts" region="just-get-heroes" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="just-get-heroes" linenums="false">
</code-example> </code-example>
You can revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service: You can revise that `HeroService` to get the heroes from the server using the Angular <code>Http</code> client service:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="v1"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="v1">
</code-example> </code-example>
Notice that the !{_Angular_Http} client service is Notice that the Angular <code>Http</code> client service is
[injected](guide/dependency-injection) into the `HeroService` constructor. [injected](guide/dependency-injection) into the `HeroService` constructor.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="ctor">
</code-example> </code-example>
Look closely at how to call `!{_priv}http.get`: Look closely at how to call `http.get`:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="http-get" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="http-get" linenums="false">
@ -238,7 +242,6 @@ The server returns heroes once you've set up the [in-memory web api](guide/serve
described in the appendix below. described in the appendix below.
Alternatively, you can temporarily target a JSON file by changing the endpoint URL: Alternatively, you can temporarily target a JSON file by changing the endpoint URL:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example> </code-example>
@ -247,13 +250,48 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U
~~~ ~~~
<a id="rxjs"></a>
If you are familiar with asynchronous methods in modern JavaScript, you might expect the `get` method to return a
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" title="Promise">promise</a>.
You'd expect to chain a call to `then()` and extract the heroes.
Instead you're calling a `map()` method.
Clearly this is not a promise.
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
and `map()` is one of the RxJS *operators*.
{@a rxjs-library}
## RxJS library
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS Reactive Extensions">RxJS</a>
is a third party library, endorsed by Angular, that implements the
<a href="https://www.youtube.com/watch?v=VLGCCpOWFFw" target="_blank" title="Video: Rob Wormald on Observables"><b>asynchronous Observable</b></a> pattern.
All of the Developer Guide samples have installed the RxJS npm package
because Observables are used widely in Angular applications.
_This_ app needs it when working with the HTTP client.
But you must take a critical extra step to make RxJS Observables usable:
_you must import the RxJS operators individually_.
### Enable RxJS operators
The RxJS library is large.
Size matters when building a production application and deploying it to mobile devices.
You should include only necessary features.
Each code file should add the operators it needs by importing from an RxJS library.
The `getHeroes()` method needs the `map()` and `catch()` operators so it imports them like this.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="rxjs-imports" linenums="false">
</code-example>
{@a extract-data} {@a extract-data}
## Process the response object ## Process the response object
Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes: Remember that the `getHeroes()` method used an `extractData()` helper method to map the `http.get` response object to heroes:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="extract-data" linenums="false">
@ -284,7 +322,7 @@ That spec defines a `json()` method that parses the response body into a JavaScr
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Don't expect the decoded JSON to be the heroes !{_array} directly. Don't expect the decoded JSON to be the heroes array directly.
This server always wraps JSON results in an object with a `data` This server always wraps JSON results in an object with a `data`
property. You have to unwrap it to get the heroes. property. You have to unwrap it to get the heroes.
This is conventional web API behavior, driven by This is conventional web API behavior, driven by
@ -314,6 +352,24 @@ The component that calls the `HeroService` only wants heroes and is kept separat
from getting them, the code dealing with where they come from, and the response object. from getting them, the code dealing with where they come from, and the response object.
~~~ {.callout.is-important}
<header>
HTTP GET is delayed
</header>
The `http.get` does **not send the request just yet.** This Observable is
[*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables),
which means that the request won't go out until something *subscribes* to the Observable.
That *something* is the [HeroListComponent](guide/server-communication#subscribe).
~~~
{@a error-handling} {@a error-handling}
### Always handle errors ### Always handle errors
@ -325,7 +381,6 @@ but only if it says something that the user can understand and act upon.
This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error. This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error.
<code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="error-handling" linenums="false">
</code-example> </code-example>
@ -347,12 +402,11 @@ logs it to the console, and returns the message in a new, failed Observable via
</h3> </h3>
Back in the `HeroListComponent`, in `!{_priv}heroService.getHeroes()`, Back in the `HeroListComponent`, in `heroService.getHeroes()`,
the `subscribe` function has a second function parameter to handle the error message. the `subscribe` function has a second function parameter to handle the error message.
It sets an `errorMessage` variable that's bound conditionally in the `HeroListComponent` template. It sets an `errorMessage` variable that's bound conditionally in the `HeroListComponent` template.
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="getHeroes" linenums="false">
</code-example> </code-example>
@ -379,7 +433,6 @@ You'll write a method for the `HeroListComponent` to call, a `create()` method,
just the name of a new hero and returns an `Observable` of `Hero`. It begins like this: just the name of a new hero and returns an `Observable` of `Hero`. It begins like this:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="create-sig" linenums="false">
</code-example> </code-example>
@ -405,6 +458,11 @@ with its own `data` property.
Now that you know how the API works, implement `create()` as follows: Now that you know how the API works, implement `create()` as follows:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="import-request-options" linenums="false">
</code-example>
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create"> <code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
@ -416,29 +474,113 @@ Now that you know how the API works, implement `create()` as follows:
### Headers ### Headers
In the `headers` object, the `Content-Type` specifies that the body represents JSON. In the `headers` object, the `Content-Type` specifies that the body represents JSON.
Next, the `headers` object is used to configure the `options` object. The `options`
object is a new instance of `RequestOptions`, a class that allows you to specify
certain settings when instantiating a request. In this way, [headers](api/http/index/Headers-class) is one of the [RequestOptions](api/http/index/RequestOptions-class).
In the `return` statement, `options` is the *third* argument of the `post()` method, as shown above.
{@a json-results} {@a json-results}
### JSON results ### JSON results
As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](guide/server-communication#extract-data) As with `getHeroes()`, use the `extractData()` helper to [extract the data](guide/server-communication#extract-data)
from the response. from the response.
Back in the `HeroListComponent`, its `addHero()` method subscribes to the Observable returned by the service's `create()` method. Back in the `HeroListComponent`, its `addHero()` method subscribes to the Observable returned by the service's `create()` method.
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user. When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
<code-example path="server-communication/src/app/toh/hero-list.component.ts" region="addHero" linenums="false"> <code-example path="server-communication/src/app/toh/hero-list.component.ts" region="addHero" linenums="false">
</code-example> </code-example>
<h2 id='promises'>
Fall back to promises
</h2>
Although the Angular `http` client API returns an `Observable<Response>` you can turn it into a
[`Promise<Response>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
It's easy to do, and in simple cases, a Promise-based version looks much
like the Observable-based version.
~~~ {.l-sub-section}
While Promises may be more familiar, Observables have many advantages.
~~~
Here is a comparison of the `HeroService` using Promises versus Observables,
highlighting just the parts that are different.
<code-tabs>
<code-pane title="src/app/toh/hero.service.promise.ts (promise-based)" path="server-communication/src/app/toh/hero.service.promise.ts" region="methods">
</code-pane>
<code-pane title="src/app/toh/hero.service.ts (observable-based)" path="server-communication/src/app/toh/hero.service.ts" region="methods">
</code-pane>
</code-tabs>
You can follow the Promise `then(this.extractData).catch(this.handleError)` pattern as in
this example.
Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the
first *success* parameter and its `catch` callback to the second *fail* parameter
in this pattern: `.toPromise(this.extractData, this.handleError)`.
The `errorHandler` forwards an error message as a failed `Promise` instead of a failed `Observable`.
The diagnostic *log to console* is just one more `then()` in the Promise chain.
You have to adjust the calling component to expect a `Promise` instead of an `Observable`:
<code-tabs>
<code-pane title="src/app/toh/hero-list.component.promise.ts (promise-based)" path="server-communication/src/app/toh/hero-list.component.promise.ts" region="methods">
</code-pane>
<code-pane title="src/app/toh/hero-list.component.ts (observable-based)" path="server-communication/src/app/toh/hero-list.component.ts" region="methods">
</code-pane>
</code-tabs>
The only obvious difference is that you call `then()` on the returned Promise instead of `subscribe`.
Both methods take the same functional arguments.
~~~ {.l-sub-section}
The less obvious but critical difference is that these two methods return very different results.
The Promise-based `then()` returns another Promise. You can keep chaining
more `then()` and `catch()` calls, getting a new promise each time.
The `subscribe()` method returns a `Subscription`. A `Subscription` is not another `Observable`.
It's the end of the line for Observables. You can't call `map()` on it or call `subscribe()` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
To understand the implications and consequences of subscriptions,
watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
~~~
<h2 id='cors'> <h2 id='cors'>
Cross-Origin Requests: Wikipedia example Cross-Origin Requests: Wikipedia example
</h2> </h2>
You just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service. You just learned how to make `XMLHttpRequests` using the Angular <code>Http</code> service.
This is the most common approach to server communication, but it doesn't work in all scenarios. This is the most common approach to server communication, but it doesn't work in all scenarios.
For security reasons, web browsers block `XHR` calls to a remote server whose origin is different from the origin of the web page. For security reasons, web browsers block `XHR` calls to a remote server whose origin is different from the origin of the web page.
@ -479,13 +621,12 @@ types in a text box:
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. This example uses the latter. Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. This example uses the latter.
The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts you to `GET` requests. The Angular `Jsonp` service both extends the `Http` service for JSONP and restricts you to `GET` requests.
All other HTTP methods throw an error because `JSONP` is a read-only facility. All other HTTP methods throw an error because `JSONP` is a read-only facility.
As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`. As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts">
</code-example> </code-example>
@ -510,21 +651,18 @@ All of this happens under the hood.
If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this: If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this:
<code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.1.ts" region="query-string" linenums="false">
</code-example> </code-example>
In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper: In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper:
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="search-parameters" linenums="false">
</code-example> </code-example>
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object. This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
<code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false"> <code-example path="server-communication/src/app/wiki/wikipedia.service.ts" region="call-jsonp" linenums="false">
</code-example> </code-example>
@ -536,7 +674,6 @@ to the server.
Now that you have a service that can query the Wikipedia API, Now that you have a service that can query the Wikipedia API,
turn your attention to the component (template and class) that takes user input and displays search results. turn your attention to the component (template and class) that takes user input and displays search results.
<code-example path="server-communication/src/app/wiki/wiki.component.ts"> <code-example path="server-communication/src/app/wiki/wiki.component.ts">
</code-example> </code-example>
@ -545,7 +682,7 @@ The template presents an `<input>` element *search box* to gather search terms f
and calls a `search(term)` method after each `keyup` event. and calls a `search(term)` method after each `keyup` event.
The component's `search(term)` method delegates to the `WikipediaService`, which returns an The component's `search(term)` method delegates to the `WikipediaService`, which returns an
Observable !{_array} of string results (`Observable<string[]>`). Observable array of string results (`Observable<string[]>`).
Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`, Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`,
the app forwards the Observable result to the template (via `items`) where the `async` pipe the app forwards the Observable result to the template (via `items`) where the `async` pipe
in the `ngFor` handles the subscription. Read more about [async pipes](guide/pipes) in the `ngFor` handles the subscription. Read more about [async pipes](guide/pipes)
@ -606,21 +743,16 @@ with the help of some nifty Observable operators.
Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`: Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`:
<code-tabs> <code-tabs>
<code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts"> <code-pane title="src/app/wiki/wiki-smart.component.ts" path="server-communication/src/app/wiki/wiki-smart.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts"> <code-pane title="src/app/wiki/wiki.component.ts" path="server-communication/src/app/wiki/wiki.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
While the templates are virtually identical, While the templates are virtually identical,
@ -637,7 +769,6 @@ The `WikiComponent` passes a new search term directly to the `WikipediaService`
The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_ The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_
with the help of a `Subject`, which you import from RxJS: with the help of a `Subject`, which you import from RxJS:
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="import-subject" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="import-subject" linenums="false">
</code-example> </code-example>
@ -646,7 +777,6 @@ The component creates a `searchTermStream` as a `Subject` of type `string`.
The `search()` method adds each new search box value to that stream via the subject's `next()` method. The `search()` method adds each new search box value to that stream via the subject's `next()` method.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="subject" linenums="false">
</code-example> </code-example>
@ -659,7 +789,6 @@ The `search()` method adds each new search box value to that stream via the subj
The `WikiSmartComponent` listens to the *stream of search terms* and The `WikiSmartComponent` listens to the *stream of search terms* and
processes that stream _before_ calling the service. processes that stream _before_ calling the service.
<code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="observable-operators" linenums="false"> <code-example path="server-communication/src/app/wiki/wiki-smart.component.ts" region="observable-operators" linenums="false">
</code-example> </code-example>
@ -718,14 +847,12 @@ This sample creates a class that sets the default `Content-Type` header to JSON.
It exports a constant with the necessary `RequestOptions` provider to simplify registration in `AppModule`. It exports a constant with the necessary `RequestOptions` provider to simplify registration in `AppModule`.
<code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false"> <code-example path="server-communication/src/app/default-request-options.service.ts" linenums="false">
</code-example> </code-example>
Then it registers the provider in the root `AppModule`. Then it registers the provider in the root `AppModule`.
<code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false"> <code-example path="server-communication/src/app/app.module.ts" region="provide-default-request-options" linenums="false">
</code-example> </code-example>
@ -741,7 +868,6 @@ Remember to include this provider during setup when unit testing the app's HTTP
After this change, the `header` option setting in `HeroService.create()` is no longer necessary, After this change, the `header` option setting in `HeroService.create()` is no longer necessary,
<code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create"> <code-example path="server-communication/src/app/toh/hero.service.ts" linenums="false" title="src/app/toh/hero.service.ts (create)" region="create">
</code-example> </code-example>
@ -762,6 +888,7 @@ It might be wise to keep the `create` request header setting for extra safety.
If the app only needed to retrieve data, you could get the heroes from a `heroes.json` file: If the app only needed to retrieve data, you could get the heroes from a `heroes.json` file:
~~~ {.l-sub-section} ~~~ {.l-sub-section}
You wrap the heroes array in an object with a `data` property for the same reason that a data server does: You wrap the heroes array in an object with a `data` property for the same reason that a data server does:
@ -772,7 +899,6 @@ posed by top-level JSON arrays.
You'd set the endpoint to the JSON file like this: You'd set the endpoint to the JSON file like this:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint-json" linenums="false">
</code-example> </code-example>
@ -797,20 +923,18 @@ for configuration options, default behaviors, and limitations.
~~~ ~~~
The in-memory web API gets its data from !{_a_ca_class_with} a `createDb()` The in-memory web API gets its data from a custom application class with a `createDb()`
method that returns a map whose keys are collection names and whose values method that returns a map whose keys are collection names and whose values
are !{_array}s of objects in those collections. are arrays of objects in those collections.
Here's the class for this sample, based on the JSON data: Here's the class for this sample, based on the JSON data:
<code-example path="server-communication/src/app/hero-data.ts" linenums="false"> <code-example path="server-communication/src/app/hero-data.ts" linenums="false">
</code-example> </code-example>
Ensure that the `HeroService` endpoint refers to the web API: Ensure that the `HeroService` endpoint refers to the web API:
<code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint" linenums="false"> <code-example path="server-communication/src/app/toh/hero.service.ts" region="endpoint" linenums="false">
</code-example> </code-example>
@ -820,7 +944,6 @@ Finally, redirect client HTTP requests to the in-memory web API by
adding the `InMemoryWebApiModule` to the `AppModule.imports` list. adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
At the same time, call its `forRoot()` configuration method with the `HeroData` class. At the same time, call its `forRoot()` configuration method with the `HeroData` class.
<code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false"> <code-example path="server-communication/src/app/app.module.ts" region="in-mem-web-api" linenums="false">
</code-example> </code-example>
@ -841,8 +964,7 @@ while setting the metadata for the root `AppModule`. Don't call it again.
~~~ ~~~
Here is the final, revised version of <span ngio-ex>src/app/app.module.ts</span>, demonstrating these steps. Here is the final, revised version of <code>src/app/app.module.ts</code>, demonstrating these steps.
<code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)"> <code-example path="server-communication/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (excerpt)">

View File

@ -14,26 +14,20 @@ This cookbook explains how to do it.**See the <live-example name="cb-set-documen
<table> <table>
<tr> <tr>
<td> <td>
To see the browser title bar change in the live example, To see the browser title bar change in the live example,
open it again in the Plunker editor by clicking the icon in the upper right, open it again in the Plunker editor by clicking the icon in the upper right,
then pop out the preview window by clicking the blue 'X' button in the upper right corner. then pop out the preview window by clicking the blue 'X' button in the upper right corner.
</td> </td>
<td> <td>
<img src='assets/images/devguide/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"> </img> <br> </br> <img src='assets/images/devguide/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"> </img> <img src='assets/images/devguide/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"> </img> <br> </br> <img src='assets/images/devguide/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"> </img>
</td> </td>
</tr> </tr>
</table> </table>
## The problem with *&lt;title&gt;* ## The problem with *&lt;title&gt;*
@ -72,7 +66,6 @@ for getting and setting the current HTML document title:
Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it: Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
<code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false"> <code-example path="cb-set-document-title/src/app/app.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -86,27 +79,20 @@ We bind that method to three anchor tags and, voilà!
Here's the complete solution Here's the complete solution
<code-tabs> <code-tabs>
<code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts"> <code-pane title="src/main.ts" path="cb-set-document-title/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="cb-set-document-title/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="cb-set-document-title/src/app/app.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

View File

@ -24,46 +24,32 @@ If you do, this page can help you understand their purpose.
<table width="100%"> <table width="100%">
<col width="10%"> <col width="10%">
</col> </col>
<col width="90%"> <col width="90%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>src/app/</code> <code>src/app/</code>
</td> </td>
<td> <td>
Angular application files go here. Angular application files go here.
@ -76,20 +62,14 @@ If you do, this page can help you understand their purpose.
as _live examples_. as _live examples_.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>e2e/</code> <code>e2e/</code>
</td> </td>
<td> <td>
_End-to-end_ (e2e) tests of the application, _End-to-end_ (e2e) tests of the application,
written in Jasmine and run by the written in Jasmine and run by the
@ -99,32 +79,22 @@ If you do, this page can help you understand their purpose.
Initialized with an e2e test for the "Hello Angular" sample. Initialized with an e2e test for the "Hello Angular" sample.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>node_modules/</code> <code>node_modules/</code>
</td> </td>
<td> <td>
The _npm_ packages installed with the `npm install` command. The _npm_ packages installed with the `npm install` command.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code> .editorconfig<br> <code> .editorconfig<br>
.git/<br> .git/<br>
@ -132,64 +102,44 @@ If you do, this page can help you understand their purpose.
.travis.yml </code> .travis.yml </code>
</td> </td>
<td> <td>
Tooling configuration files and folders. Tooling configuration files and folders.
Ignore them until you have a compelling reason to do otherwise. Ignore them until you have a compelling reason to do otherwise.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>CHANGELOG.md</code> <code>CHANGELOG.md</code>
</td> </td>
<td> <td>
The history of changes to the _QuickStart_ repository. The history of changes to the _QuickStart_ repository.
Delete or ignore. Delete or ignore.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>favicon.ico</code> <code>favicon.ico</code>
</td> </td>
<td> <td>
The application icon that appears in the browser tab. The application icon that appears in the browser tab.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>index.html</code> <code>index.html</code>
</td> </td>
<td> <td>
The application host page. The application host page.
It loads a few essential scripts in a prescribed order. It loads a few essential scripts in a prescribed order.
@ -199,58 +149,40 @@ If you do, this page can help you understand their purpose.
The same `index.html` satisfies all documentation application samples. The same `index.html` satisfies all documentation application samples.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma.conf.js</code> <code>karma.conf.js</code>
</td> </td>
<td> <td>
Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a> Configuration for the <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
test runner described in the [Testing](guide/testing) guide. test runner described in the [Testing](guide/testing) guide.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>karma-test-shim.js</code> <code>karma-test-shim.js</code>
</td> </td>
<td> <td>
Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a> Script to run <a href="https://karma-runner.github.io/1.0/index.html" target="_blank" title="Karma unit test runner">karma</a>
with SystemJS as described in the [Testing](guide/testing) guide. with SystemJS as described in the [Testing](guide/testing) guide.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>non-essential-files.txt</code> <code>non-essential-files.txt</code>
</td> </td>
<td> <td>
A list of files that you can delete if you want to purge your setup of the A list of files that you can delete if you want to purge your setup of the
original QuickStart Seed testing and git maintainence artifacts. original QuickStart Seed testing and git maintainence artifacts.
@ -259,38 +191,26 @@ If you do, this page can help you understand their purpose.
*Do this only in the beginning to avoid accidentally deleting your own tests and git setup!* *Do this only in the beginning to avoid accidentally deleting your own tests and git setup!*
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>LICENSE</code> <code>LICENSE</code>
</td> </td>
<td> <td>
The open source MIT license to use this setup code in your application. The open source MIT license to use this setup code in your application.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>package.json</code> <code>package.json</code>
</td> </td>
<td> <td>
Identifies `npm `package dependencies for the project. Identifies `npm `package dependencies for the project.
@ -300,142 +220,98 @@ If you do, this page can help you understand their purpose.
target="_blank" title="npm scripts for Angular documentation samples">Read more</a> about them. target="_blank" title="npm scripts for Angular documentation samples">Read more</a> about them.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>protractor.config.js</code> <code>protractor.config.js</code>
</td> </td>
<td> <td>
Configuration for the Configuration for the
<a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a> <a href="http://www.protractortest.org/" target="_blank" title="Protractor: end-to-end testing for Angular">protractor</a>
_end-to-end_ (e2e) test runner. _end-to-end_ (e2e) test runner.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>README.md</code> <code>README.md</code>
</td> </td>
<td> <td>
Instruction for using this git repository in your project. Instruction for using this git repository in your project.
Worth reading before deleting. Worth reading before deleting.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>styles.css</code> <code>styles.css</code>
</td> </td>
<td> <td>
Global styles for the application. Initialized with an `<h1>` style for the QuickStart demo. Global styles for the application. Initialized with an `<h1>` style for the QuickStart demo.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>systemjs<br>.config.js</code> <code>systemjs<br>.config.js</code>
</td> </td>
<td> <td>
Tells the **SystemJS** module loader where to find modules Tells the **SystemJS** module loader where to find modules
referenced in JavaScript `import` statements. For example: referenced in JavaScript `import` statements. For example:
<code-example language="ts"> <code-example language="ts">
import { Component } from '@angular/core; import { Component } from '@angular/core;
</code-example> </code-example>
Don't touch this file unless you are fully versed in SystemJS configuration. Don't touch this file unless you are fully versed in SystemJS configuration.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>systemjs<br>.config.extras.js</code> <code>systemjs<br>.config.extras.js</code>
</td> </td>
<td> <td>
Optional extra SystemJS configuration. Optional extra SystemJS configuration.
A way to add SystemJS mappings, such as for appliation _barrels_, A way to add SystemJS mappings, such as for appliation _barrels_,
without changing the original `system.config.js`. without changing the original `system.config.js`.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tsconfig.json</code> <code>tsconfig.json</code>
</td> </td>
<td> <td>
Tells the TypeScript compiler how to transpile TypeScript source files Tells the TypeScript compiler how to transpile TypeScript source files
into JavaScript files that run in all modern browsers. into JavaScript files that run in all modern browsers.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<code>tslint.json</code> <code>tslint.json</code>
</td> </td>
<td> <td>
The `npm` installed TypeScript linter inspects your TypeScript code The `npm` installed TypeScript linter inspects your TypeScript code
and complains when you violate one of its rules. and complains when you violate one of its rules.
@ -444,9 +320,7 @@ If you do, this page can help you understand their purpose.
[Angular style guide](guide/style-guide) and by the authors of the documentation. [Angular style guide](guide/style-guide) and by the authors of the documentation.
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -11,20 +11,18 @@ Install the Angular QuickStart seed for faster, more efficient development on yo
{@a develop-locally} {@a develop-locally}
## Setup a local development environment ## Setup a local development environment
<span if-docs="ts">
The <live-example name=quickstart>QuickStart live-coding</live-example> example is an Angular _playground_. The <live-example name=quickstart>QuickStart live-coding</live-example> example is an Angular _playground_.
It's not where you'd develop a real application. It's not where you'd develop a real application.
You [should develop locally](guide/setup#why-locally "Why develop locally") on your own machine ... and that's also how we think you should learn Angular. You [should develop locally](guide/setup#why-locally "Why develop locally") on your own machine ... and that's also how we think you should learn Angular.
</span>
Setting up a new project on your machine is quick and easy with the **QuickStart seed**, Setting up a new project on your machine is quick and easy with the **QuickStart seed**,
maintained [on github](guide/!{_qsRepo} "Install the github QuickStart repo"). maintained [on github](https://github.com/angular/quickstart "Install the github QuickStart repo").
Make sure you have [!{_prereq} installed](guide/setup#install-prerequisites "What if you don't have !{_prereq}?"). Make sure you have [node and npm installed](guide/setup#install-prerequisites "What if you don't have node and npm?").
Then ... Then ...
1. Create a project folder (you can call it `quickstart` and rename it later). 1. Create a project folder (you can call it `quickstart` and rename it later).
1. [Clone](guide/setup#clone "Clone it from github") or [download](guide/setup#download "download it from github") the **QuickStart seed** into your project folder. 1. [Clone](guide/setup#clone "Clone it from github") or [download](guide/setup#download "download it from github") the **QuickStart seed** into your project folder.
1. !{_Install} [!{_npm}](guide/setup#install-prerequisites "What if you don't have !{_prereq}?") packages. 1. Install [npm](guide/setup#install-prerequisites "What if you don't have node and npm?") packages.
1. Run `!{_npm} !{_start}` to launch the sample application. 1. Run `npm start` to launch the sample application.
{@a clone} {@a clone}
@ -34,10 +32,10 @@ Perform the _clone-to-launch_ steps with these terminal commands.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
git clone .git quickstart git clone https://github.com/angular/quickstart.git quickstart
cd quickstart cd quickstart
npm install
npm start
</code-example> </code-example>
@ -54,14 +52,14 @@ Perform the _clone-to-launch_ steps with these terminal commands.
{@a download} {@a download}
### Download ### Download
<a href="!{_qsRepoZip}" title="Download the QuickStart seed repository">Download the QuickStart seed</a> <a href="https://github.com/angular/quickstart/archive/master.zip" title="Download the QuickStart seed repository">Download the QuickStart seed</a>
and unzip it into your project folder. Then perform the remaining steps with these terminal commands. and unzip it into your project folder. Then perform the remaining steps with these terminal commands.
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
cd quickstart cd quickstart
npm install
npm start
</code-example> </code-example>
@ -130,61 +128,44 @@ Focus on the following three TypeScript (`.ts`) files in the **`/src`** folder.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="setup/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="setup/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="setup/src/main.ts"> <code-pane title="src/main.ts" path="setup/src/main.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
All guides and cookbooks have _at least these core files_. All guides and cookbooks have _at least these core files_.
@ -208,86 +189,60 @@ The following are all in `src/`
<table width="100%"> <table width="100%">
<col width="20%"> <col width="20%">
</col> </col>
<col width="80%"> <col width="80%">
</col> </col>
<tr> <tr>
<th> <th>
File File
</th> </th>
<th> <th>
Purpose Purpose
</th> </th>
</tr> </tr>
<tr> <tr>
<td> <td>
<ngio-ex>app/app.component.ts</ngio-ex> <code>app/app.component.ts</code>
</td> </td>
<td> <td>
Defines the same `AppComponent` as the one in the QuickStart !{_playground}. Defines the same `AppComponent` as the one in the QuickStart playground.
It is the **root** component of what will become a tree of nested components It is the **root** component of what will become a tree of nested components
as the application evolves. as the application evolves.
</td> </td>
</tr> </tr>
<tr>
<tr if-docs="ts">
<td> <td>
<code>app/app.module.ts</code> <code>app/app.module.ts</code>
</td> </td>
<td> <td>
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application. Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`. Right now it declares only the `AppComponent`.
Soon there will be more components to declare. Soon there will be more components to declare.
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<ngio-ex>main.ts</ngio-ex> <code>main.ts</code>
</td> </td>
<td> <td>
Compiles the application with the [JIT compiler](glossary) and Compiles the application with the [JIT compiler](glossary) and
[bootstraps](guide/appmodule) [bootstraps](guide/appmodule)
@ -298,10 +253,8 @@ The following are all in `src/`
</td> </td>
</tr> </tr>
</table> </table>
@ -319,7 +272,7 @@ If you're new to Angular, we recommend staying on the [learning path](guide/lear
{@a install-prerequisites} {@a install-prerequisites}
## Appendix: !{_prereq} ## Appendix: node and npm
Node.js and npm are essential to modern web development with Angular and other platforms. Node.js and npm are essential to modern web development with Angular and other platforms.
Node powers client development and build tools. Node powers client development and build tools.
The _npm_ package manager, itself a _node_ application, installs JavaScript libraries. The _npm_ package manager, itself a _node_ application, installs JavaScript libraries.
@ -334,3 +287,32 @@ Older versions produce errors.
We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm. We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm.
You may need [nvm](https://github.com/creationix/nvm) if you already have projects running on your machine that You may need [nvm](https://github.com/creationix/nvm) if you already have projects running on your machine that
use other versions of node and npm. use other versions of node and npm.
{@a why-locally}
## Appendix: Why develop locally
<live-example title="QuickStart Seed in Plunker">Live coding</live-example> in the browser is a great way to explore Angular.
Links on almost every documentation page open completed samples in the browser.
You can play with the sample code, share your changes with friends, and download and run the code on your own machine.
The [QuickStart](quickstart) shows just the `AppComponent` file.
It creates the equivalent of `app.module.ts` and `main.ts` internally _for the playground only_.
so the reader can discover Angular without distraction.
The other samples are based on the QuickStart seed.
As much fun as this is ...
* you can't ship your app in plunker
* you aren't always online when writing code
* transpiling TypeScript in the browser is slow
* the type support, refactoring, and code completion only work in your local IDE
Use the <live-example title="QuickStart Seed in Plunker"><i>live coding</i></live-example> environment as a _playground_,
a place to try the documentation samples and experiment on your own.
It's the perfect place to reproduce a bug when you want to
<a href="https://github.com/angular/angular.io/issues/new" target="_blank" title="File a documentation issue">file a documentation issue</a> or
<a href="https://github.com/angular/angular/issues/new" target="_blank" title="File an Angular issue">file an issue with Angular itself</a>.
For real development, we strongly recommend [developing locally](guide/setup#develop-locally).

View File

@ -50,7 +50,6 @@ Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example. An asterisk (*) precedes the directive attribute name as in this example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example> </code-example>
@ -70,7 +69,6 @@ described in the [_Template Syntax_](guide/template-syntax) guide and seen in sa
Here's an example of them in a template: Here's an example of them in a template:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in">
</code-example> </code-example>
@ -132,7 +130,6 @@ You can [only apply one](guide/structural-directives#one-per-element) _structura
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear. It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-true)" region="ngif-true"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-true)" region="ngif-true">
</code-example> </code-example>
@ -158,7 +155,6 @@ The component and DOM nodes can be garbage-collected and free up memory.
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`. A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (display-none)" region="display-none"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (display-none)" region="display-none">
</code-example> </code-example>
@ -204,7 +200,6 @@ and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists. Here is `*ngIf` displaying the hero's name if `hero` exists.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (asterisk)" region="asterisk"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (asterisk)" region="asterisk">
</code-example> </code-example>
@ -214,7 +209,6 @@ Internally, Angular desugars it in two stages.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this. First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr">
</code-example> </code-example>
@ -222,7 +216,6 @@ First, it translates the `*ngIf="..."` into a template _attribute_, `template="n
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this. Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template">
</code-example> </code-example>
@ -254,7 +247,6 @@ template _attribute_ to template _element_.
Here's a full-featured application of `NgFor`, written all three ways: Here's a full-featured application of `NgFor`, written all three ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example> </code-example>
@ -364,7 +356,6 @@ The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`,
Here's an example. Here's an example.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example> </code-example>
@ -395,7 +386,6 @@ As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form. can be desugared into the template _attribute_ form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr">
</code-example> </code-example>
@ -403,7 +393,6 @@ can be desugared into the template _attribute_ form.
That, in turn, can be desugared into the `<template>` element form. That, in turn, can be desugared into the `<template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
</code-example> </code-example>
@ -436,7 +425,6 @@ those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!". That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (template-tag)" region="template-tag"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (template-tag)" region="template-tag">
</code-example> </code-example>
@ -463,7 +451,6 @@ There's often a _root_ element that can and should host the structural directive
The list element (`<li>`) is a typical host element of an `NgFor` repeater. The list element (`<li>`) is a typical host element of an `NgFor` repeater.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngfor-li)" region="ngfor-li"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngfor-li)" region="ngfor-li">
</code-example> </code-example>
@ -472,7 +459,6 @@ When there isn't a host element, you can usually wrap the content in a native HT
such as a `<div>`, and attach the directive to that wrapper. such as a `<div>`, and attach the directive to that wrapper.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
</code-example> </code-example>
@ -486,7 +472,6 @@ neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout. For example, suppose you have the following paragraph layout.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-span)" region="ngif-span"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-span)" region="ngif-span">
</code-example> </code-example>
@ -494,7 +479,6 @@ For example, suppose you have the following paragraph layout.
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph. You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
<code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span"> <code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span">
</code-example> </code-example>
@ -515,7 +499,6 @@ You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this, When you try this,
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-span)" region="select-span"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-span)" region="select-span">
</code-example> </code-example>
@ -537,7 +520,6 @@ because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`. Here's the conditional paragraph again, this time using `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example> </code-example>
@ -552,7 +534,6 @@ It renders properly.
Now conditionally exclude a _select_ `<option>` with `<ng-container>`. Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example> </code-example>
@ -593,7 +574,6 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***. `UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1">
</code-example> </code-example>
@ -611,7 +591,6 @@ Creating a directive is similar to creating a component.
Here's how you might begin: Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (skeleton)" region="skeleton">
</code-example> </code-example>
@ -643,7 +622,6 @@ and access the _view container_ through a
You inject both in the directive constructor as private variables of the class. You inject both in the directive constructor as private variables of the class.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor">
</code-example> </code-example>
@ -663,7 +641,6 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax) guide.
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (set)" region="set"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (set)" region="set">
</code-example> </code-example>
@ -682,17 +659,15 @@ Nobody reads the `myUnless` property so it doesn't need a getter.
The completed directive code looks like this: The completed directive code looks like this:
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (excerpt)" region="no-docs"> <code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example> </code-example>
Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleVsAppComp}. Add this directive to the `declarations` array of the AppModule.
Then create some HTML to try it. Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless">
</code-example> </code-example>
@ -716,51 +691,36 @@ You can both try and download the source code for this guide in the <live-exampl
Here is the source from the `src/app/` folder. Here is the source from the `src/app/` folder.
<code-tabs> <code-tabs>
<code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts"> <code-pane title="app.component.ts" path="structural-directives/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app.component.html" path="structural-directives/src/app/app.component.html"> <code-pane title="app.component.html" path="structural-directives/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="app.component.css" path="structural-directives/src/app/app.component.css"> <code-pane title="app.component.css" path="structural-directives/src/app/app.component.css">
</code-pane> </code-pane>
<code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts"> <code-pane title="app.module.ts" path="structural-directives/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="hero.ts" path="structural-directives/src/app/hero.ts"> <code-pane title="hero.ts" path="structural-directives/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts"> <code-pane title="hero-switch.components.ts" path="structural-directives/src/app/hero-switch.components.ts">
</code-pane> </code-pane>
<code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts"> <code-pane title="unless.directive.ts" path="structural-directives/src/app/unless.directive.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
You learned You learned

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

@ -19,16 +19,16 @@ can read and write Angular apps in their preferred dialect.
{@a toc} {@a toc}
## Table of contents ## Table of contents
[_TypeScript_ to _ES6_ to _ES5_](guide/ts-to-js#from-ts)<br> * [_TypeScript_ to _ES6_ to _ES5_](guide/ts-to-js#from-ts)<br>
[Modularity: imports and exports](guide/ts-to-js#modularity)<br> * [Modularity: imports and exports](guide/ts-to-js#modularity)<br>
[Classes and Class Metadata](guide/ts-to-js#class-metadata)<br> * [Classes and Class Metadata](guide/ts-to-js#class-metadata)<br>
[_ES5_ DSL](guide/ts-to-js#dsl)<br> * [_ES5_ DSL](guide/ts-to-js#dsl)<br>
[Interfaces](guide/ts-to-js#interfaces)<br> * [Interfaces](guide/ts-to-js#interfaces)<br>
[Input and Output Metadata](guide/ts-to-js#io-decorators)<br> * [Input and Output Metadata](guide/ts-to-js#io-decorators)<br>
[Dependency Injection](guide/ts-to-js#dependency-injection)<br> * [Dependency Injection](guide/ts-to-js#dependency-injection)<br>
[Host Binding](guide/ts-to-js#host-binding)<br> * [Host Binding](guide/ts-to-js#host-binding)<br>
[View and Child Decorators](guide/ts-to-js#view-child-decorators)<br> * [View and Child Decorators](guide/ts-to-js#view-child-decorators)<br>
[AOT compilation in _TypeScript_ Only](guide/ts-to-js#aot)<br> * [AOT compilation in _TypeScript_ Only](guide/ts-to-js#aot)<br>
**Run and compare the live <live-example name="cb-ts-to-js">_TypeScript_</live-example> and <live-example name="cb-ts-to-js" lang="js">JavaScript</live-example> **Run and compare the live <live-example name="cb-ts-to-js">_TypeScript_</live-example> and <live-example name="cb-ts-to-js" lang="js">JavaScript</live-example>
code shown in this cookbook.** code shown in this cookbook.**
@ -86,33 +86,24 @@ through the global `ng` object.
Anything you can import from `@angular` is a nested member of this `ng` object: Anything you can import from `@angular` is a nested member of this `ng` object:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="ng2import"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="ng2import">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="ng2import">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Exporting Application Code ### Exporting Application Code
@ -143,33 +134,24 @@ to limit unintentional leaking of private symbols into the global scope.
Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants. Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="appexport"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="appexport">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="appexport">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Importing Application Code ### Importing Application Code
@ -179,33 +161,24 @@ In _TypeScript_ and _ES6_ apps, you `import` things that have been exported from
In _ES5_ you use the shared namespace object to access "exported" entities from other files. In _ES5_ you use the shared namespace object to access "exported" entities from other files.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/app.module.ts" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="appimport"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/app.module.es6" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/app.module.es6" region="appimport">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/app.module.js" region="appimport">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -250,33 +223,24 @@ _ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype. Use the constructor function pattern instead, adding methods to the prototype.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="class">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="class"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="class">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="class">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="constructorproto">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Metadata ### Metadata
@ -295,73 +259,54 @@ In _ES5_, you also provide an `annotations` array but you attach it to the _cons
See these variations side-by-side: See these variations side-by-side:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero.component.ts" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="metadata"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero.component.es6" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero.component.es6" region="metadata">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js" region="metadata">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
***External Template file*** ***External Template file***
A large component template is often kept in a separate template file. A large component template is often kept in a separate template file.
<code-example path="cb-ts-to-js/ts/src/app/hero-title.component.html" linenums="false"> <code-example path="cb-ts-to-js/ts/src/app/hero-title.component.html" linenums="false">
</code-example> </code-example>
The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property: The component (`HeroTitleComponent` in this case) then references the template file in its metadata `templateUrl` property:
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6" region="templateUrl"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6" region="templateUrl">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="templateUrl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
Note that both the _TypeScript_ and _ES6_ `templateUrl` properties identify the location of the template file _relative to the component module_. Note that both the _TypeScript_ and _ES6_ `templateUrl` properties identify the location of the template file _relative to the component module_.
@ -385,21 +330,16 @@ Here is an example of the `HeroComponent`, re-written with the DSL,
next to the original _ES5_ version for comparison: next to the original _ES5_ version for comparison:
<code-tabs> <code-tabs>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero.component.js" region="dsl">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero.component.js">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -426,7 +366,6 @@ _TypeScript_ and _ES6_ support with getters and setters.
Here's an example of a read-only _TypeScript_ property with a getter Here's an example of a read-only _TypeScript_ property with a getter
that prepares a toggle-button label for the next clicked state: that prepares a toggle-button label for the next clicked state:
<code-example path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="defined-property" linenums="false"> <code-example path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="defined-property" linenums="false">
</code-example> </code-example>
@ -438,7 +377,6 @@ The _ES5 DSL_ does not support _defined properties_ directly
but you can still create them by extracting the "class" prototype and but you can still create them by extracting the "class" prototype and
adding the _defined property_ in raw JavaScript like this: adding the _defined property_ in raw JavaScript like this:
<code-example path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="defined-property" linenums="false"> <code-example path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="defined-property" linenums="false">
</code-example> </code-example>
@ -483,39 +421,28 @@ They have no physical manifestation in the generated JavaScript code.
Just implement the methods and ignore interfaces when translating code samples from _TypeScript_ to JavaScript. Just implement the methods and ignore interfaces when translating code samples from _TypeScript_ to JavaScript.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-lifecycle.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-lifecycle.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-lifecycle.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-lifecycle.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -540,39 +467,28 @@ But note that what would have been _separate_ `@Input` and `@Output` property de
combined in the metadata `inputs` and `outputs` _arrays_. combined in the metadata `inputs` and `outputs` _arrays_.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/confirm.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/confirm.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/confirm.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/confirm.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/confirm.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
In the previous example, one of the public-facing binding names (`cancelMsg`) In the previous example, one of the public-facing binding names (`cancelMsg`)
@ -618,39 +534,28 @@ last parameter is the class constructor itself.
This format should be familiar to AngularJS developers. This format should be familiar to AngularJS developers.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Injection with the @Inject decorator ### Injection with the @Inject decorator
@ -672,39 +577,28 @@ When writing with _ES5 DSL_, set the `Class.constructor` property to a function
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter. array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-di-inject.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-di-inject.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-di-inject.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-di-inject.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Additional Injection Decorators ### Additional Injection Decorators
@ -728,39 +622,28 @@ When writing with _ES5 DSL_, set the `Class.constructor` property to a function
array as before. Use a nested array to define a parameter's complete injection specification. array as before. Use a nested array to define a parameter's complete injection specification.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-title.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-title.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-title.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-title.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-title.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -802,39 +685,28 @@ The `host` value is an object whose properties are host property and listener b
* Each value identifies the corresponding component property or method. * Each value identifies the corresponding component property or method.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host.component.es6">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-host.component.es6">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js"> <code-pane title="ES5 JavaScript" path="cb-ts-to-js/js/src/app/hero-host.component.js">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-host.component.js" region="dsl"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-host.component.js" region="dsl">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
### Host Metadata ### Host Metadata
@ -846,21 +718,16 @@ The following re-implementation of the `HeroComponent` reminds us that _any prop
can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_. can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_.
These particular _TypeScript_ and _ES6_ code snippets happen to be identical. These particular _TypeScript_ and _ES6_ code snippets happen to be identical.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-host-meta.component.ts">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-host-meta.component.es6">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -893,33 +760,24 @@ The `queries` property value is a hash map.
* each _value_ is a new instance of either `ViewChild` or `ViewChildren`. * each _value_ is a new instance of either `ViewChild` or `ViewChildren`.
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="view">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="view"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="view">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="view">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="view"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="view">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
The [`@ContentChild`](api/core/index/ContentChild-decorator) and The [`@ContentChild`](api/core/index/ContentChild-decorator) and
@ -931,33 +789,24 @@ They can be added in the same way as [`@ViewChild`](api/core/index/ViewChild-dec
[`@ViewChildren`](api/core/index/ViewChildren-decorator). [`@ViewChildren`](api/core/index/ViewChildren-decorator).
<code-tabs> <code-tabs>
<code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content"> <code-pane title="TypeScript" path="cb-ts-to-js/ts/src/app/hero-queries.component.ts" region="content">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="content"> <code-pane title="ES6 JavaScript with decorators" path="cb-ts-to-js/js-es6-decorators/src/app/hero-queries.component.es6" region="content">
</code-pane> </code-pane>
<code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content"> <code-pane title="ES6 JavaScript" path="cb-ts-to-js/js-es6/src/app/hero-queries.component.es6" region="content">
</code-pane> </code-pane>
<code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="content"> <code-pane title="ES5 JavaScript with DSL" path="cb-ts-to-js/js/src/app/hero-queries.component.js" region="content">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,6 @@ To bind to a DOM event, surround the DOM event name in parentheses and assign a
The following example shows an event binding that implements a click handler: The following example shows an event binding that implements a click handler:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false"> <code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" linenums="false">
</code-example> </code-example>
@ -39,7 +38,6 @@ usually the Angular component controlling the template.
The example above shows a single line of HTML, but that HTML belongs to a larger component: The example above shows a single line of HTML, but that HTML belongs to a larger component:
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false"> <code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" linenums="false">
</code-example> </code-example>
@ -52,7 +50,6 @@ This section shows how to bind to the `keyup` event of an input box to get the u
The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler. The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" linenums="false">
</code-example> </code-example>
@ -60,7 +57,6 @@ The following code listens to the `keyup` event and passes the entire event payl
When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding
DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method. DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" linenums="false">
</code-example> </code-example>
@ -119,7 +115,6 @@ that could reveal properties of the event object and prevent silly mistakes.
The following example rewrites the method with types: The following example rewrites the method with types:
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" linenums="false">
</code-example> </code-example>
@ -146,7 +141,6 @@ To declare a template reference variable, precede an identifier with a hash (or
The following example uses a template reference variable The following example uses a template reference variable
to implement a keystroke loopback in a simple template. to implement a keystroke loopback in a simple template.
<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" linenums="false"> <code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" linenums="false">
</code-example> </code-example>
@ -185,7 +179,6 @@ It's easier to get to the input box with the template reference
variable than to go through the `$event` object. Here's a rewrite of the previous variable than to go through the `$event` object. Here's a rewrite of the previous
`keyup` example that uses a template reference variable to get the user's input. `keyup` example that uses a template reference variable to get the user's input.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" linenums="false">
</code-example> </code-example>
@ -201,7 +194,6 @@ One way to reduce the noise would be to examine every `$event.keyCode` and take
There's an easier way: bind to Angular's `keyup.enter` pseudo-event. There's an easier way: bind to Angular's `keyup.enter` pseudo-event.
Then Angular calls the event handler only when the user presses _Enter_. Then Angular calls the event handler only when the user presses _Enter_.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" linenums="false">
</code-example> </code-example>
@ -223,7 +215,6 @@ The component's `value` property is updated only when the user presses _Enter_.
To fix this issue, listen to both the _Enter_ key and the _blur_ event. To fix this issue, listen to both the _Enter_ key and the _blur_ event.
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false"> <code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" linenums="false">
</code-example> </code-example>
@ -246,7 +237,6 @@ clicking **Add**.
Below is the "Little Tour of Heroes" component. Below is the "Little Tour of Heroes" component.
<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false"> <code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" linenums="false">
</code-example> </code-example>
@ -270,33 +260,24 @@ clears the input box after a new hero is added to the list.
Following is all the code discussed in this page. Following is all the code discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts"> <code-pane title="click-me.component.ts" path="user-input/src/app/click-me.component.ts">
</code-pane> </code-pane>
<code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts"> <code-pane title="keyup.components.ts" path="user-input/src/app/keyup.components.ts">
</code-pane> </code-pane>
<code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts"> <code-pane title="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts">
</code-pane> </code-pane>
<code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts"> <code-pane title="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

View File

@ -70,7 +70,6 @@ You supply Webpack with one or more *entry* files and let it find and incorporat
The one entry point file in this example is the application's root file, `src/main.ts`: The one entry point file in this example is the application's root file, `src/main.ts`:
<code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="one-entry" linenums="false">
</code-example> </code-example>
@ -78,7 +77,6 @@ The one entry point file in this example is the application's root file, `src/ma
Webpack inspects that file and traverses its `import` dependencies recursively. Webpack inspects that file and traverses its `import` dependencies recursively.
<code-example path="webpack/src/app/app.component.ts" region="component" linenums="false"> <code-example path="webpack/src/app/app.component.ts" region="component" linenums="false">
</code-example> </code-example>
@ -91,7 +89,6 @@ Then it **outputs** these files to the `app.js` _bundle file_ designated in conf
<div class='code-example'> <div class='code-example'>
<code-example name="webpack.config.js (single output)" language="javascript"> <code-example name="webpack.config.js (single output)" language="javascript">
output: { output: {
filename: 'app.js' filename: 'app.js'
@ -99,7 +96,6 @@ Then it **outputs** these files to the `app.js` _bundle file_ designated in conf
</code-example> </code-example>
</div> </div>
This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies. This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies.
@ -116,7 +112,6 @@ Change the configuration so that it has two entry points, `main.ts` and `vendor.
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
entry: { entry: {
app: 'src/app.ts', app: 'src/app.ts',
@ -129,7 +124,6 @@ Change the configuration so that it has two entry points, `main.ts` and `vendor.
</code-example> </code-example>
</div> </div>
Webpack constructs two separate dependency graphs Webpack constructs two separate dependency graphs
@ -148,7 +142,6 @@ The `[name]` in the output name is a *placeholder* that a Webpack plugin replace
To tell Webpack what belongs in the vendor bundle, To tell Webpack what belongs in the vendor bundle,
add a `vendor.ts` file that only imports the application's third-party modules: add a `vendor.ts` file that only imports the application's third-party modules:
<code-example path="webpack/src/vendor.ts" linenums="false"> <code-example path="webpack/src/vendor.ts" linenums="false">
</code-example> </code-example>
@ -167,7 +160,6 @@ Configure loaders for TypeScript and CSS as follows.
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
rules: [ rules: [
{ {
@ -182,7 +174,6 @@ Configure loaders for TypeScript and CSS as follows.
</code-example> </code-example>
</div> </div>
When Webpack encounters `import` statements like the following, When Webpack encounters `import` statements like the following,
@ -191,7 +182,6 @@ it applies the `test` RegEx patterns.
<div class='code-example'> <div class='code-example'>
<code-example language="typescript"> <code-example language="typescript">
import { AppComponent } from './app.component.ts'; import { AppComponent } from './app.component.ts';
@ -199,7 +189,6 @@ it applies the `test` RegEx patterns.
</code-example> </code-example>
</div> </div>
When a pattern matches the filename, Webpack processes the file with the associated loader. When a pattern matches the filename, Webpack processes the file with the associated loader.
@ -223,7 +212,6 @@ Tap into that pipeline with plugins such as the `uglify` minification plugin:
<div class='code-example'> <div class='code-example'>
<code-example language="javascript"> <code-example language="javascript">
plugins: [ plugins: [
new webpack.optimize.UglifyJsPlugin() new webpack.optimize.UglifyJsPlugin()
@ -231,7 +219,6 @@ Tap into that pipeline with plugins such as the `uglify` minification plugin:
</code-example> </code-example>
</div> </div>
@ -255,39 +242,28 @@ Create a new project folder.
Add these files: Add these files:
<code-tabs> <code-tabs>
<code-pane title="package.json" path="webpack/package.webpack.json"> <code-pane title="package.json" path="webpack/package.webpack.json">
</code-pane> </code-pane>
<code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json"> <code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json">
</code-pane> </code-pane>
<code-pane title="webpack.config.js" path="webpack/webpack.config.js"> <code-pane title="webpack.config.js" path="webpack/webpack.config.js">
</code-pane> </code-pane>
<code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js"> <code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js">
</code-pane> </code-pane>
<code-pane title="config/helpers.js" path="webpack/config/helpers.js"> <code-pane title="config/helpers.js" path="webpack/config/helpers.js">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
@ -324,7 +300,6 @@ Polyfills should be bundled separately from the application and vendor bundles.
Add a `polyfills.ts` like this one to the `src/` folder. Add a `polyfills.ts` like this one to the `src/` folder.
<code-example path="webpack/src/polyfills.ts" linenums="false"> <code-example path="webpack/src/polyfills.ts" linenums="false">
</code-example> </code-example>
@ -358,7 +333,6 @@ All three have a lot of configuration in common.
Gather the common configuration in a file called `webpack.common.js`. Gather the common configuration in a file called `webpack.common.js`.
<code-example path="webpack/config/webpack.common.js" linenums="false"> <code-example path="webpack/config/webpack.common.js" linenums="false">
</code-example> </code-example>
@ -384,7 +358,6 @@ and exports several objects as properties of a `module.exports` object.
The first export is the `entry` object: The first export is the `entry` object:
<code-example path="webpack/config/webpack.common.js" region="entries" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="entries" linenums="false">
</code-example> </code-example>
@ -405,13 +378,11 @@ You could write `import` statements with explicit extensions like this example:
<div class='code-example'> <div class='code-example'>
<code-example language="typescript"> <code-example language="typescript">
import { AppComponent } from './app.component.ts'; import { AppComponent } from './app.component.ts';
</code-example> </code-example>
</div> </div>
But most `import` statements don't mention the extension at all. But most `import` statements don't mention the extension at all.
@ -419,7 +390,6 @@ Tell Webpack to resolve extension-less file requests by looking for matching fil
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files). `.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
<code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="resolve" linenums="false">
</code-example> </code-example>
@ -441,7 +411,6 @@ add `.css` and `.html` to the list.
Rules tell Webpack which loaders to use for each file, or module: Rules tell Webpack which loaders to use for each file, or module:
<code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="loaders" linenums="false">
</code-example> </code-example>
@ -481,7 +450,6 @@ Multiple loaders can be chained using the array notation.
Finally, create instances of three plugins: Finally, create instances of three plugins:
<code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false"> <code-example path="webpack/config/webpack.common.js" region="plugins" linenums="false">
</code-example> </code-example>
@ -534,7 +502,6 @@ These files tend to be short and simple.
Here is the `webpack.dev.js` development configuration file. Here is the `webpack.dev.js` development configuration file.
<code-example path="webpack/config/webpack.dev.js" linenums="false"> <code-example path="webpack/config/webpack.dev.js" linenums="false">
</code-example> </code-example>
@ -572,7 +539,6 @@ Grab the app code at the end of this guide and try:
Configuration of a *production* build resembles *development* configuration with a few key changes. Configuration of a *production* build resembles *development* configuration with a few key changes.
<code-example path="webpack/config/webpack.prod.js" linenums="false"> <code-example path="webpack/config/webpack.prod.js" linenums="false">
</code-example> </code-example>
@ -596,7 +562,6 @@ There are additional plugins:
Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this: Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this:
<code-example path="webpack/src/main.ts" region="enable-prod" linenums="false"> <code-example path="webpack/src/main.ts" region="enable-prod" linenums="false">
</code-example> </code-example>
@ -624,7 +589,6 @@ You could merge the test configuration into the `webpack.common` configuration a
But it might be simpler to start over with a completely fresh configuration. But it might be simpler to start over with a completely fresh configuration.
<code-example path="webpack/config/webpack.test.js" linenums="false"> <code-example path="webpack/config/webpack.test.js" linenums="false">
</code-example> </code-example>
@ -632,7 +596,6 @@ But it might be simpler to start over with a completely fresh configuration.
Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests: Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests:
<code-example path="webpack/config/karma.conf.js" linenums="false"> <code-example path="webpack/config/karma.conf.js" linenums="false">
</code-example> </code-example>
@ -644,7 +607,6 @@ The `karma-test-shim` tells Karma what files to pre-load and
primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded. primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded.
<code-example path="webpack/config/karma-test-shim.js" linenums="false"> <code-example path="webpack/config/karma-test-shim.js" linenums="false">
</code-example> </code-example>
@ -667,68 +629,50 @@ Here is the source code for a small application that bundles with the
Webpack techniques covered in this guide. Webpack techniques covered in this guide.
<code-tabs> <code-tabs>
<code-pane title="src/index.html" path="webpack/src/index.html"> <code-pane title="src/index.html" path="webpack/src/index.html">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="webpack/src/main.ts"> <code-pane title="src/main.ts" path="webpack/src/main.ts">
</code-pane> </code-pane>
<code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css"> <code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css"> <code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts"> <code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
The <code>app.component.html</code> displays this downloadable Angular logo The <code>app.component.html</code> displays this downloadable Angular logo
<a href="https://raw.githubusercontent.com/angular/angular.io/master/publicassets/images/logos/angular/angular.png" target="_blank"> <a href="assets/images/logos/angular/angular.png" target="_blank">
<img src="assets/images/logos/angular/angular.png" height="40px" title="download Angular logo"></a>. <img src="assets/images/logos/angular/angular.png" height="40px" title="download Angular logo"></a>.
Create a folder called `images` under the project's `assets` folder, then right-click (Cmd+click on Mac) Create a folder called `images` under the project's `assets` folder, then right-click (Cmd+click on Mac)
on the image and download it to that folder. on the image and download it to that folder.
@ -737,21 +681,16 @@ on the image and download it to that folder.
{@a bundle-ts} {@a bundle-ts}
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles. Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
<code-tabs> <code-tabs>
<code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts"> <code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts">
</code-pane> </code-pane>
<code-pane title="src/vendor.ts" path="webpack/src/vendor.ts"> <code-pane title="src/vendor.ts" path="webpack/src/vendor.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
<a id="highlights"></a>### Highlights <a id="highlights"></a>### Highlights

View File

@ -7,85 +7,61 @@ Build a simple hero editor.
@description @description
## Setup to develop locally ## Setup to develop locally
Follow the [setup](guide/setup) instructions for creating a new project Follow the [setup](guide/setup) instructions for creating a new project
named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>. named <code>angular-tour-of-heroes</code>.
The file structure should look like this: The file structure should look like this:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
When you're done with this page, the app should look like this <live-example></live-example>. When you're done with this page, the app should look like this <live-example></live-example>.
@ -111,7 +87,6 @@ Add two properties to the `AppComponent`: a `title` property for the app name an
for a hero named "Windstorm." for a hero named "Windstorm."
<code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false">
</code-example> </code-example>
@ -119,7 +94,6 @@ for a hero named "Windstorm."
Now update the template in the `@Component` decorator with data bindings to these new properties. Now update the template in the `@Component` decorator with data bindings to these new properties.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false">
</code-example> </code-example>
@ -147,7 +121,6 @@ Create a `Hero` class with `id` and `name` properties.
Add these properties near the top of the `app.component.ts` file, just below the import statement. Add these properties near the top of the `app.component.ts` file, just below the import statement.
<code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false"> <code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false">
</code-example> </code-example>
@ -156,7 +129,6 @@ In the `Hero` class, refactor the component's `hero` property to be of type `Her
then initialize it with an `id` of `1` and the name `Windstorm`. then initialize it with an `id` of `1` and the name `Windstorm`.
<code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false"> <code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false">
</code-example> </code-example>
@ -165,7 +137,6 @@ Because you changed the hero from a string to an object,
update the binding in the template to refer to the hero's `name` property. update the binding in the template to refer to the hero's `name` property.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero-2"> <code-example path="toh-1/app/app.component.1.ts" region="show-hero-2">
</code-example> </code-example>
@ -184,7 +155,6 @@ thanks to the <i>template literals</i> feature in ES2015 and TypeScript. For mor
<code-example path="toh-1/app/app.component.1.ts" region="multi-line-strings" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="multi-line-strings" linenums="false">
</code-example> </code-example>
@ -202,7 +172,6 @@ You need a two-way binding between the `<input>` form element and the `hero.name
Refactor the hero name in the template so it looks like this: Refactor the hero name in the template so it looks like this:
<code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false"> <code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false">
</code-example> </code-example>
@ -228,7 +197,6 @@ of external modules that the app uses.
The updated `AppModule` looks like this: The updated `AppModule` looks like this:
<code-example path="toh-1/src/app/app.module.ts"> <code-example path="toh-1/src/app/app.module.ts">
</code-example> </code-example>
@ -264,7 +232,6 @@ Your app should look like this <live-example></live-example>.
Here's the complete `app.component.ts` as it stands now: Here's the complete `app.component.ts` as it stands now:
<code-example path="toh-1/src/app/app.component.ts"> <code-example path="toh-1/src/app/app.component.ts">
</code-example> </code-example>

View File

@ -18,78 +18,54 @@ If your structure doesn't match, go back to that page to figure out what you mis
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
## Keep the app transpiling and running ## Keep the app transpiling and running
@ -113,7 +89,6 @@ To display a list of heroes, you'll add heroes to the view's template.
Create an array of ten heroes. Create an array of ten heroes.
<code-example path="toh-2/src/app/app.component.ts" region="hero-array"> <code-example path="toh-2/src/app/app.component.ts" region="hero-array">
</code-example> </code-example>
@ -127,7 +102,6 @@ Create a public property in `AppComponent` that exposes the heroes for binding.
<code-example path="toh-2/app/app.component.1.html" region="hero-array-1"> <code-example path="toh-2/app/app.component.1.html" region="hero-array-1">
</code-example> </code-example>
@ -149,7 +123,6 @@ insert the following chunk of HTML below the title and above the hero details.
<code-example path="toh-2/app/app.component.1.html" region="heroes-template-1" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="heroes-template-1" linenums="false">
</code-example> </code-example>
@ -164,7 +137,6 @@ and display them individually.
Modify the `<li>` tag by adding the built-in directive `*ngFor`. Modify the `<li>` tag by adding the built-in directive `*ngFor`.
<code-example path="toh-2/app/app.component.1.html" region="heroes-ngfor-1"> <code-example path="toh-2/app/app.component.1.html" region="heroes-ngfor-1">
</code-example> </code-example>
@ -198,7 +170,6 @@ that uses the `hero` template variable to display the hero's properties.
<code-example path="toh-2/app/app.component.1.html" region="ng-for" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="ng-for" linenums="false">
</code-example> </code-example>
@ -212,7 +183,6 @@ To add styles to your component, set the `styles` property on the `@Component` d
to the following CSS classes: to the following CSS classes:
<code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false"> <code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false">
</code-example> </code-example>
@ -228,7 +198,6 @@ The template for displaying heroes should look like this:
<code-example path="toh-2/app/app.component.1.html" region="heroes-styled" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="heroes-styled" linenums="false">
</code-example> </code-example>
@ -249,7 +218,6 @@ Add a click event binding to the `<li>` like this:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-click" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="selectedHero-click" linenums="false">
</code-example> </code-example>
@ -275,7 +243,6 @@ But the user will be able to select one of the heroes by clicking on it.
So replace the `hero` property with this simple `selectedHero` property: So replace the `hero` property with this simple `selectedHero` property:
<code-example path="toh-2/src/app/app.component.ts" region="selected-hero"> <code-example path="toh-2/src/app/app.component.ts" region="selected-hero">
</code-example> </code-example>
@ -285,7 +252,6 @@ you won't initialize the `selectedHero` as you did with `hero`.
Add an `onSelect` method that sets the `selectedHero` property to the `hero` that the user clicks. Add an `onSelect` method that sets the `selectedHero` property to the `hero` that the user clicks.
<code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false"> <code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false">
</code-example> </code-example>
@ -295,7 +261,6 @@ Bind to the new selectedHero property instead as follows:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-details" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="selectedHero-details" linenums="false">
</code-example> </code-example>
@ -319,7 +284,6 @@ Wrap the HTML hero detail content of the template with a `<div>`.
Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component. Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component.
<code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false">
</code-example> </code-example>
@ -368,7 +332,6 @@ like this:
In the template, add the following `[class.selected]` binding to the `<li>`: In the template, add the following `[class.selected]` binding to the `<li>`:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-1" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="class-selected-1" linenums="false">
</code-example> </code-example>
@ -387,7 +350,6 @@ Read more about the `[class]` binding in the [Template Syntax](guide/template-sy
The final version of the `<li>` looks like this: The final version of the `<li>` looks like this:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false"> <code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false">
</code-example> </code-example>
@ -402,7 +364,6 @@ After clicking "Magneta", the list should look like this:
Here's the complete `app.component.ts` as of now: Here's the complete `app.component.ts` as of now:
<code-example path="toh-2/src/app/app.component.ts"> <code-example path="toh-2/src/app/app.component.ts">
</code-example> </code-example>

View File

@ -24,78 +24,54 @@ If not, go back to the previous pages.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
Keep the app transpiling and running while you build the Tour of Heroes Keep the app transpiling and running while you build the Tour of Heroes
@ -119,7 +95,6 @@ The `HeroDetailComponent` class goes in the `hero-detail.component.ts` file.
Start writing the `HeroDetailComponent` as follows: Start writing the `HeroDetailComponent` as follows:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false">
</code-example> </code-example>
@ -145,7 +120,6 @@ Replace the word, "selectedHero", with the word, "hero", everywhere in the templ
When you're done, the new template should look like this: When you're done, the new template should look like this:
<code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false">
</code-example> </code-example>
@ -155,7 +129,6 @@ When you're done, the new template should look like this:
The `HeroDetailComponent` template binds to the component's `hero` property. The `HeroDetailComponent` template binds to the component's `hero` property.
Add that property to the `HeroDetailComponent` class like this: Add that property to the `HeroDetailComponent` class like this:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="hero">
</code-example> </code-example>
@ -168,7 +141,6 @@ The Angular [style guide](guide/style-guide) recommends one class per file anywa
Move the `Hero` class from `app.component.ts` to its own `hero.ts` file. Move the `Hero` class from `app.component.ts` to its own `hero.ts` file.
<code-example path="toh-3/src/app/hero.ts" linenums="false"> <code-example path="toh-3/src/app/hero.ts" linenums="false">
</code-example> </code-example>
@ -176,7 +148,6 @@ Move the `Hero` class from `app.component.ts` to its own `hero.ts` file.
Now that the `Hero` class is in its own file, the `AppComponent` and the `HeroDetailComponent` have to import it. Now that the `Hero` class is in its own file, the `AppComponent` and the `HeroDetailComponent` have to import it.
Add the following `import` statement near the top of _both_ the `app.component.ts` and the `hero-detail.component.ts` files. Add the following `import` statement near the top of _both_ the `app.component.ts` and the `hero-detail.component.ts` files.
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero-import"> <code-example path="toh-3/app/hero-detail.component.1.ts" region="hero-import">
</code-example> </code-example>
@ -188,7 +159,6 @@ the parent `AppComponent` will tell the child `HeroDetailComponent` which hero t
by binding its `selectedHero` to the `hero` property of the `HeroDetailComponent`. by binding its `selectedHero` to the `hero` property of the `HeroDetailComponent`.
The binding will look like this: The binding will look like this:
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false"> <code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example> </code-example>
@ -200,7 +170,6 @@ Otherwise, Angular rejects the binding and throws an error.
First, amend the `@angular/core` import statement to include the `Input` symbol. First, amend the `@angular/core` import statement to include the `Input` symbol.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false">
</code-example> </code-example>
@ -208,7 +177,6 @@ First, amend the `@angular/core` import statement to include the `Input` symbol.
Then declare that `hero` is an *input* property by Then declare that `hero` is an *input* property by
preceding it with the `@Input` decorator that you imported earlier. preceding it with the `@Input` decorator that you imported earlier.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="hero" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="hero" linenums="false">
</code-example> </code-example>
@ -225,7 +193,6 @@ Read more about _input_ properties in the
That's it. The `hero` property is the only thing in the `HeroDetailComponent` class. That's it. The `hero` property is the only thing in the `HeroDetailComponent` class.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false"> <code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false">
</code-example> </code-example>
@ -234,7 +201,6 @@ All it does is receive a hero object through its `hero` input property and then
Here's the complete `HeroDetailComponent`. Here's the complete `HeroDetailComponent`.
<code-example path="toh-3/src/app/hero-detail.component.ts"> <code-example path="toh-3/src/app/hero-detail.component.ts">
</code-example> </code-example>
@ -245,7 +211,6 @@ Every component must be declared in one&mdash;and only one&mdash;Angular module.
Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you can refer to it. Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you can refer to it.
<code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import"> <code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import">
</code-example> </code-example>
@ -253,7 +218,6 @@ Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you
Add `HeroDetailComponent` to the module's `declarations` array. Add `HeroDetailComponent` to the module's `declarations` array.
<code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false"> <code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false">
</code-example> </code-example>
@ -289,7 +253,6 @@ Coordinate the master `AppComponent` with the `HeroDetailComponent`
by binding the `selectedHero` property of the `AppComponent` by binding the `selectedHero` property of the `AppComponent`
to the `hero` property of the `HeroDetailComponent`. to the `hero` property of the `HeroDetailComponent`.
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false"> <code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example> </code-example>
@ -299,7 +262,6 @@ Now every time the `selectedHero` changes, the `HeroDetailComponent` gets a new
The revised `AppComponent` template should look like this: The revised `AppComponent` template should look like this:
<code-example path="toh-3/src/app/app.component.ts" region="hero-detail-template" linenums="false"> <code-example path="toh-3/src/app/app.component.ts" region="hero-detail-template" linenums="false">
</code-example> </code-example>
@ -327,122 +289,85 @@ Verify that you have the following structure:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
Here are the code files discussed in this page. Here are the code files discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts"> <code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts"> <code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>

View File

@ -14,7 +14,7 @@ Using a separate service keeps components lean and focused on supporting the vie
and makes it easy to unit-test components with a mock service. and makes it easy to unit-test components with a mock service.
Because data services are invariably asynchronous, Because data services are invariably asynchronous,
you'll finish the page with a *!{_Promise}*-based version of the data service. you'll finish the page with a *Promise*-based version of the data service.
When you're done with this page, the app should look like this <live-example></live-example>. When you're done with this page, the app should look like this <live-example></live-example>.
@ -25,90 +25,62 @@ If not, go back to the previous pages.
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
## Keep the app transpiling and running ## Keep the app transpiling and running
@ -151,7 +123,6 @@ For example, the filename for `SpecialSuperHeroService` is `special-super-hero.s
Name the class `HeroService` and export it for others to import. Name the class `HeroService` and export it for others to import.
<code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false">
</code-example> </code-example>
@ -175,7 +146,6 @@ consistency and future-proofing.
Add a `getHeroes()` method stub. Add a `getHeroes()` method stub.
<code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false">
</code-example> </code-example>
@ -191,7 +161,6 @@ Cut the `HEROES` array from `app.component.ts` and paste it to a new file in the
Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class. Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class.
<code-example path="toh-4/src/app/mock-heroes.ts"> <code-example path="toh-4/src/app/mock-heroes.ts">
</code-example> </code-example>
@ -201,7 +170,6 @@ The `HEROES` constant is exported so it can be imported elsewhere, such as the `
In `app.component.ts`, where you cut the `HEROES` array, In `app.component.ts`, where you cut the `HEROES` array,
add an uninitialized `heroes` property: add an uninitialized `heroes` property:
<code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false">
</code-example> </code-example>
@ -210,7 +178,6 @@ add an uninitialized `heroes` property:
Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method. Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method.
The `HeroService` looks like this: The `HeroService` looks like this:
<code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false"> <code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false">
</code-example> </code-example>
@ -220,7 +187,6 @@ You're ready to use the `HeroService` in other components, starting with `AppCom
Import the `HeroService` so that you can reference it in the code. Import the `HeroService` so that you can reference it in the code.
<code-example path="toh-4/src/app/app.component.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (hero-service-import)" region="hero-service-import"> <code-example path="toh-4/src/app/app.component.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (hero-service-import)" region="hero-service-import">
</code-example> </code-example>
@ -230,7 +196,6 @@ How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
You could create a new instance of the `HeroService` with `new` like this: You could create a new instance of the `HeroService` with `new` like this:
<code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false">
</code-example> </code-example>
@ -257,7 +222,6 @@ Instead of using the *new* line, you'll add two lines.
Add the constructor: Add the constructor:
<code-example path="toh-4/src/app/app.component.1.ts" region="ctor"> <code-example path="toh-4/src/app/app.component.1.ts" region="ctor">
</code-example> </code-example>
@ -285,7 +249,6 @@ in the `@Component` call.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (providers)" region="providers"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (providers)" region="providers">
</code-example> </code-example>
@ -299,7 +262,6 @@ The service is in a `heroService` private variable.
You could call the service and get the data in one line. You could call the service and get the data in one line.
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -307,7 +269,6 @@ You could call the service and get the data in one line.
You don't really need a dedicated method to wrap one line. Write it anyway: You don't really need a dedicated method to wrap one line. Write it anyway:
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (getHeroes)" region="getHeroes"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (getHeroes)" region="getHeroes">
</code-example> </code-example>
@ -334,7 +295,6 @@ Read more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks)
Here's the essential outline for the `OnInit` interface (don't copy this into your code): Here's the essential outline for the `OnInit` interface (don't copy this into your code):
<code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false">
</code-example> </code-example>
@ -348,18 +308,16 @@ Add the implementation for the `OnInit` interface to your export statement:
Write an `ngOnInit` method with the initialization logic inside. Angular will call it Write an `ngOnInit` method with the initialization logic inside. Angular will call it
at the right time. In this case, initialize by calling `getHeroes()`. at the right time. In this case, initialize by calling `getHeroes()`.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (ng-on-init)" region="ng-on-init"> <code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (ng-on-init)" region="ng-on-init">
</code-example> </code-example>
The app should run as expected, showing a list of heroes and a hero detail view The app should run as expected, showing a list of heroes and a hero detail view
when you click on a hero name. when you click on a hero name.
<a id="async"></a>## Async services and !{_Promise}s <a id="async"></a>## Async services and Promises
The `HeroService` returns a list of mock heroes immediately; The `HeroService` returns a list of mock heroes immediately;
its `getHeroes()` signature is synchronous. its `getHeroes()` signature is synchronous.
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
@ -368,12 +326,12 @@ Eventually, the hero data will come from a remote server.
When using a remote server, users don't have to wait for the server to respond; When using a remote server, users don't have to wait for the server to respond;
additionally, you aren't able to block the UI during the wait. additionally, you aren't able to block the UI during the wait.
To coordinate the view with the response, To coordinate the view with the response,
you can use *!{_Promise}s*, which is an asynchronous you can use *Promises*, which is an asynchronous
technique that changes the signature of the `getHeroes()` method. technique that changes the signature of the `getHeroes()` method.
### The hero service makes a !{_Promise} ### The hero service makes a Promise
A *!{_Promise}* essentially promises to call back when the results are ready. A *Promise* essentially promises to call back when the results are ready.
You ask an asynchronous service to do some work and give it a callback function. You ask an asynchronous service to do some work and give it a callback function.
The service does that work and eventually calls the function with the results or an error. The service does that work and eventually calls the function with the results or an error.
@ -386,30 +344,27 @@ This is a simplified explanation. Read more about ES2015 Promises in the
~~~ ~~~
Update the `HeroService` with this !{_Promise}-returning `getHeroes()` method: Update the `HeroService` with this Promise-returning `getHeroes()` method:
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false">
</code-example> </code-example>
You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server, You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server,
by returning an *immediately resolved !{_Promise}* with the mock heroes as the result. by returning an *immediately resolved Promise* with the mock heroes as the result.
### Act on the !{_Promise} ### Act on the Promise
As a result of the change to `HeroService`, `this.heroes` is now set to a !{_Promise} rather than an array of heroes.
As a result of the change to `HeroService`, `this.heroes` is now set to a `Promise` rather than an array of heroes.
<code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false"> <code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false">
</code-example> </code-example>
You have to change the implementation to *act on the !{_Promise} when it resolves*. You have to change the implementation to *act on the `Promise` when it resolves*.
When the !{_Promise} resolves successfully, you'll have heroes to display. When the `Promise` resolves successfully, you'll have heroes to display.
Pass the callback function as an argument to the !{_Promise}'s `then()` method:
Pass the callback function as an argument to the Promise's `then()` method:
<code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false"> <code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false">
@ -442,128 +397,89 @@ Verify that you have the following structure after all of your refactoring:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
mock-heroes.ts mock-heroes.ts
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
Here are the code files discussed in this page. Here are the code files discussed in this page.
<code-tabs> <code-tabs>
<code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts"> <code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts"> <code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
## The road you've travelled ## The road you've travelled
@ -573,7 +489,7 @@ Here's what you achieved in this page:
* You used the `ngOnInit` lifecycle hook to get the hero data when the `AppComponent` activates. * You used the `ngOnInit` lifecycle hook to get the hero data when the `AppComponent` activates.
* You defined the `HeroService` as a provider for the `AppComponent`. * You defined the `HeroService` as a provider for the `AppComponent`.
* You created mock hero data and imported them into the service. * You created mock hero data and imported them into the service.
* You designed the service to return a !{_Promise} and the component to get the data from the !{_Promise}. * You designed the service to return a Promise and the component to get the data from the Promise.
Your app should look like this <live-example></live-example>. Your app should look like this <live-example></live-example>.
@ -588,13 +504,12 @@ Read about the Angular component router and navigation among the views in the [n
To simulate a slow connection, To simulate a slow connection,
import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`. import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`.
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes-slowly" linenums="false"> <code-example path="toh-4/src/app/hero.service.ts" region="get-heroes-slowly" linenums="false">
</code-example> </code-example>
Like `getHeroes()`, it also returns a !{_Promise}. Like `getHeroes()`, it also returns a `Promise`.
But this !{_Promise} waits two seconds before resolving the !{_Promise} with mock heroes. But this Promise waits two seconds before resolving the Promise with mock heroes.
Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()` Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()`
and see how the app behaves. and see how the app behaves.

File diff suppressed because it is too large Load Diff

View File

@ -24,14 +24,12 @@ That's the starting point for this page.
## Keep the app transpiling and running ## Keep the app transpiling and running
Enter the following command in the terminal window: Enter the following command in the terminal window:
code-example(language="sh" class="code-shell").
<code-example language="sh" class="code-shell">
npm start npm start
</code-example> :marked
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes. The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser. You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
@ -40,14 +38,12 @@ You can keep building the Tour of Heroes without pausing to recompile or refresh
Providing HTTP Services Providing HTTP Services
</h1> </h1>
The `HttpModule` is not a core Angular module. The `HttpModule` is not a core Angular module.
`HttpModule` is Angular's optional approach to web access. It exists as a separate add-on module called `@angular/http` `HttpModule` is Angular's optional approach to web access. It exists as a separate add-on module called `@angular/http`
and is shipped in a separate script file as part of the Angular npm package. and is shipped in a separate script file as part of the Angular npm package.
You're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when you need it. You're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when you need it.
## Register for HTTP services ## Register for HTTP services
The app will depend on the Angular `http` service, which itself depends on other supporting services. The app will depend on the Angular `http` service, which itself depends on other supporting services.
The `HttpModule` from the `@angular/http` library holds providers for a complete set of HTTP services. The `HttpModule` from the `@angular/http` library holds providers for a complete set of HTTP services.
@ -55,52 +51,48 @@ To allow access to these services from anywhere in the app,
add `HttpModule` to the `imports` list of the `AppModule`. add `HttpModule` to the `imports` list of the `AppModule`.
<code-example path="toh-6/src/app/app.module.ts" region="v1">
<code-example path="toh-pt6/src/app/app.module.ts" region="v1">
</code-example> </code-example>
Notice that you also supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`. Notice that you also supply `HttpModule` as part of the *imports* array in root NgModule `AppModule`.
## Simulate the web API ## Simulate the web API
We recommend registering app-wide services in the root
`AppModule` *providers*.
Until you have a web server that can handle requests for hero data, Until you have a web server that can handle requests for hero data,
the HTTP client will fetch and save data from the HTTP client will fetch and save data from
a mock service, the *in-memory web API*. a mock service, the *in-memory web API*.
Update <span ngio-ex>!{_appModuleTsVsMainTs}</span> with this version, which uses the mock service: Update <code>src/app/app.module.ts</code> with this version, which uses the mock service:
<code-example path="toh-6/src/app/app.module.ts" region="v2">
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (v2)" region="v2">
</code-example> </code-example>
Rather than require a real API server, this example simulates communication with the remote server by adding the Rather than require a real API server, this example simulates communication with the remote server by adding the
<a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API">InMemoryWebApiModule</a> <a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API">InMemoryWebApiModule</a>
to the module `imports`, effectively replacing the `Http` client's XHR backend service with an in-memory alternative. to the module `imports`, effectively replacing the `Http` client's XHR backend service with an in-memory alternative.
<code-example path="toh-6/src/app/app.module.ts" region="in-mem-web-api">
<code-example path="toh-pt6/_appModuleTsVsMainTs" linenums="false" title="_appModuleTsVsMainTs (in-mem-web-api)" region="in-mem-web-api">
</code-example> </code-example>
The `forRoot()` configuration method takes an `InMemoryDataService` class The `forRoot()` configuration method takes an `InMemoryDataService` class
that primes the in-memory database. that primes the in-memory database.
Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content: Add the file `in-memory-data.service.ts` in `app` with the following content:
<code-example path="toh-6/src/app/in-memory-data.service.ts" region="init" linenums="false">
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" linenums="false">
</code-example> </code-example>
This file replaces `mock-heroes.ts`, which is now safe to delete. This file replaces `mock-heroes.ts`, which is now safe to delete.
~~~ {.alert.is-helpful} ~~~ {.alert.is-helpful}
The in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes. The in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
@ -117,11 +109,10 @@ section of the [HTTP Client](guide/server-communication) page.
## Heroes and HTTP ## Heroes and HTTP
In the current `HeroService` implementation, a !{_Promise} resolved with mock heroes is returned. In the current `HeroService` implementation, a Promise resolved with mock heroes is returned.
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes">
<code-example path="toh-4/src/app/hero.service.ts" linenums="false" title="toh-4/ts/src/app/hero.service.ts (old getHeroes)" region="get-heroes">
</code-example> </code-example>
@ -131,25 +122,21 @@ fetching heroes with an HTTP client, which must be an asynchronous operation.
Now convert `getHeroes()` to use HTTP. Now convert `getHeroes()` to use HTTP.
<code-example path="toh-6/src/app/hero.service.ts" region="getHeroes">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated getHeroes and new class members)" region="getHeroes">
</code-example> </code-example>
Update the import statements as follows: Update the import statements as follows:
<code-example path="toh-6/src/app/hero.service.ts" region="imports">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (updated imports)" region="imports">
</code-example> </code-example>
Refresh the browser. The hero data should successfully load from the Refresh the browser. The hero data should successfully load from the
mock server. mock server.
<h3 id="!{_h3id}">HTTP !{_Promise}</h3> <h3 id="http-promise">HTTP Promise</h3>
The Angular `http.get` returns an RxJS `Observable`. The Angular `http.get` returns an RxJS `Observable`.
*Observables* are a powerful way to manage asynchronous data flows. *Observables* are a powerful way to manage asynchronous data flows.
You'll read about [Observables](tutorial/toh-pt6#observables) later in this page. You'll read about [Observables](tutorial/toh-pt6#observables) later in this page.
@ -157,8 +144,7 @@ You'll read about [Observables](tutorial/toh-pt6#observables) later in this page
For now, you've converted the `Observable` to a `Promise` using the `toPromise` operator. For now, you've converted the `Observable` to a `Promise` using the `toPromise` operator.
<code-example path="toh-6/src/app/hero.service.ts" region="to-promise">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-promise)" region="to-promise">
</code-example> </code-example>
@ -169,8 +155,7 @@ To use those capabilities, you have to add the operators themselves.
That's as easy as importing them from the RxJS library like this: That's as easy as importing them from the RxJS library like this:
<code-example path="toh-6/src/app/hero.service.ts" region="rxjs">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (rxjs)" region="rxjs">
</code-example> </code-example>
@ -189,14 +174,13 @@ In the *Promise*'s `then()` callback, you call the `json` method of the HTTP `Re
data within the response. data within the response.
<code-example path="toh-6/src/app/hero.service.ts" region="to-data">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (to-data)" region="to-data">
</code-example> </code-example>
The response JSON has a single `data` property, which The response JSON has a single `data` property, which
holds the !{_array} of heroes that the caller wants. holds the array of heroes that the caller wants.
So you grab that !{_array} and return it as the resolved !{_Promise} value. So you grab that array and return it as the resolved Promise value.
~~~ {.alert.is-important} ~~~ {.alert.is-important}
@ -209,15 +193,14 @@ Your API might return something else. Adjust the code to match your web API.
~~~ ~~~
The caller is unaware that you fetched the heroes from the (mock) server. The caller is unaware that you fetched the heroes from the (mock) server.
It receives a !{_Promise} of *heroes* just as it did before. It receives a Promise of *heroes* just as it did before.
### Error Handling ### Error Handling
At the end of `getHeroes()`, you `catch` server failures and pass them to an error handler. At the end of `getHeroes()`, you `catch` server failures and pass them to an error handler.
<code-example path="toh-6/src/app/hero.service.ts" region="catch">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (catch)" region="catch">
</code-example> </code-example>
@ -225,8 +208,7 @@ This is a critical step.
You must anticipate HTTP failures, as they happen frequently for reasons beyond your control. You must anticipate HTTP failures, as they happen frequently for reasons beyond your control.
<code-example path="toh-6/src/app/hero.service.ts" region="handleError">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (handleError)" region="handleError">
</code-example> </code-example>
@ -234,7 +216,7 @@ This demo service logs the error to the console; in real life,
you would handle the error in code. For a demo, this works. you would handle the error in code. For a demo, this works.
The code also includes an error to The code also includes an error to
the caller in a !{rejected_promise}, so that the caller can display a proper error message to the user. the caller in a rejected promise, so that the caller can display a proper error message to the user.
### Get hero by id ### Get hero by id
@ -247,21 +229,20 @@ Most web APIs support a _get-by-id_ request in the form `api/hero/:id` (such as
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-6/src/app/hero.service.ts" region="getHero">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (getHero)" region="getHero">
</code-example> </code-example>
This request is almost the same as `getHeroes()`. This request is almost the same as `getHeroes()`.
The hero id in the URL identifies which hero the server should update. The hero id in the URL identifies which hero the server should update.
Also, the `data` in the response is a single hero object rather than !{_an} !{_array}. Also, the `data` in the response is a single hero object rather than an array.
### Unchanged _getHeroes_ API ### Unchanged _getHeroes_ API
Although you made significant internal changes to `getHeroes()` and `getHero()`, Although you made significant internal changes to `getHeroes()` and `getHero()`,
the public signatures didn't change. the public signatures didn't change.
You still return a !{_Promise} from both methods. You still return a Promise from both methods.
You won't have to update any of the components that call them. You won't have to update any of the components that call them.
Now it's time to add the ability to create and delete heroes. Now it's time to add the ability to create and delete heroes.
@ -284,8 +265,7 @@ 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-6/src/app/hero-detail.component.html" region="save">
<code-example path="toh-pt6/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (save)" region="save">
</code-example> </code-example>
@ -293,8 +273,7 @@ Add the following `save()` method, which persists hero name changes using the he
`update()` method and then navigates back to the previous view. `update()` method and then navigates back to the previous view.
<code-example path="toh-6/src/app/hero-detail.component.ts" region="save">
<code-example path="toh-pt6/src/app/hero-detail.component.ts" linenums="false" title="src/app/hero-detail.component.ts (save)" region="save">
</code-example> </code-example>
@ -305,14 +284,13 @@ The overall structure of the `update()` method is similar to that of
<code-example path="toh-6/src/app/hero.service.ts" region="update">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (update)" region="update">
</code-example> </code-example>
To identify which hero the server should update, the hero `id` is encoded in To identify which hero the server should update, the hero `id` is encoded in
the URL. The `put()` body is the JSON string encoding of the hero, obtained by the URL. The `put()` body is the JSON string encoding of the hero, obtained by
calling `!{_JSON_stringify}`. The body content type calling `JSON.stringify`. The body content type
(`application/json`) is identified in the request header. (`application/json`) is identified in the request header.
Refresh the browser, change a hero name, save your change, Refresh the browser, change a hero name, save your change,
@ -327,8 +305,7 @@ Insert the following into the heroes component HTML, just after
the heading: the heading:
<code-example path="toh-6/src/app/heroes.component.html" region="add">
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (add)" region="add">
</code-example> </code-example>
@ -336,18 +313,16 @@ In response to a click event, call the component's click handler and then
clear the input field so that it's ready for another name. clear the input field so that it's ready for another name.
<code-example path="toh-6/src/app/heroes.component.ts" region="add">
<code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (add)" region="add">
</code-example> </code-example>
When the given name is non-blank, the handler delegates creation of the When the given name is non-blank, the handler delegates creation of the
named hero to the hero service, and then adds the new hero to the !{_array}. named hero to the hero service, and then adds the new hero to the array.
Implement the `create()` method in the `HeroService` class. Implement the `create()` method in the `HeroService` class.
<code-example path="toh-6/src/app/hero.service.ts" region="create">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (create)" region="create">
</code-example> </code-example>
@ -361,16 +336,14 @@ Add the following button element to the heroes component HTML, after the hero
name in the repeated `<li>` element. name in the repeated `<li>` element.
<code-example path="toh-6/src/app/heroes.component.html" region="delete">
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (delete)" region="delete">
</code-example> </code-example>
The `<li>` element should now look like this: The `<li>` element should now look like this:
<code-example path="toh-6/src/app/heroes.component.html" region="li-element">
<code-example path="toh-pt6/src/app/heroes.component.html" linenums="false" title="src/app/heroes.component.html (li-element)" region="li-element">
</code-example> </code-example>
@ -382,20 +355,18 @@ select the hero that the user will delete.
The logic of the `delete()` handler is a bit trickier: The logic of the `delete()` handler is a bit trickier:
<code-example path="toh-6/src/app/heroes.component.ts" region="delete">
<code-example path="toh-pt6/src/app/heroes.component.ts" linenums="false" title="src/app/heroes.component.ts (delete)" region="delete">
</code-example> </code-example>
Of course you delegate hero deletion to the hero service, but the component Of course you delegate hero deletion to the hero service, but the component
is still responsible for updating the display: it removes the deleted hero is still responsible for updating the display: it removes the deleted hero
from the !{_array} and resets the selected hero, if necessary. from the array and resets the selected hero, if necessary.
To place the delete button at the far right of the hero entry, To place the delete button at the far right of the hero entry,
add this CSS: add this CSS:
<code-example path="toh-6/src/app/heroes.component.css" region="additions">
<code-example path="toh-pt6/src/app/heroes.component.css" linenums="false" title="src/app/heroes.component.css (additions)" region="additions">
</code-example> </code-example>
@ -404,8 +375,7 @@ add this CSS:
Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server: Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server:
<code-example path="toh-6/src/app/hero.service.ts" region="delete">
<code-example path="toh-pt6/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (delete)" region="delete">
</code-example> </code-example>
@ -416,8 +386,7 @@ Refresh the browser and try the new delete functionality.
</div> </div>
## !{_Observable}s ## Observables
Each `Http` service method returns an `Observable` of HTTP `Response` objects. Each `Http` service method returns an `Observable` of HTTP `Response` objects.
The `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller. The `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
@ -440,8 +409,8 @@ The calling component can easily consume a single result in the form of a Promis
But requests aren't always done only once. But requests aren't always done only once.
You may start one request, You may start one request,
cancel it, and make a different request before the server has responded to the first request. cancel it, and make a different request before the server has responded to the first request.
A *request-cancel-new-request* sequence is difficult to implement with *!{_Promise}s*, but A *request-cancel-new-request* sequence is difficult to implement with *Promises*, but
easy with *!{_Observable}s*. easy with *Observables*.
### Add the ability to search by name ### Add the ability to search by name
You're going to add a *hero search* feature to the Tour of Heroes. You're going to add a *hero search* feature to the Tour of Heroes.
@ -450,15 +419,14 @@ As the user types a name into a search box, you'll make repeated HTTP requests f
Start by creating `HeroSearchService` that sends search queries to the server's web API. Start by creating `HeroSearchService` that sends search queries to the server's web API.
<code-example path="toh-6/src/app/hero-search.service.ts">
<code-example path="toh-pt6/src/app/hero-search.service.ts">
</code-example> </code-example>
The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one The `http.get()` call in `HeroSearchService` is similar to the one
in the `HeroService`, although the URL now has a query string. in the `HeroService`, although the URL now has a query string.
<span if-docs="ts">More importantly, you no longer call `toPromise()`. More importantly, you no longer call `toPromise()`.
Instead you return the *Observable* from the the `htttp.get()`, Instead you return the *Observable* from the the `htttp.get()`,
after chaining it to another RxJS operator, <code>map()</code>, after chaining it to another RxJS operator, <code>map()</code>,
to extract heroes from the response data. to extract heroes from the response data.
@ -471,15 +439,13 @@ Create a `HeroSearchComponent` that calls the new `HeroSearchService`.
The component template is simple&mdash;just a text box and a list of matching search results. The component template is simple&mdash;just a text box and a list of matching search results.
<code-example path="toh-6/src/app/hero-search.component.html">
<code-example path="toh-pt6/src/app/hero-search.component.html">
</code-example> </code-example>
Also, add styles for the new component. Also, add styles for the new component.
<code-example path="toh-6/src/app/hero-search.component.css">
<code-example path="toh-pt6/src/app/hero-search.component.css">
</code-example> </code-example>
@ -488,29 +454,26 @@ method with the new search box value.
As expected, the `*ngFor` repeats hero objects from the component's `heroes` property. As expected, the `*ngFor` repeats hero objects from the component's `heroes` property.
But as you'll soon see, the `heroes` property is now !{_an} *!{_Observable}* of hero !{_array}s, rather than just a hero !{_array}. But as you'll soon see, the `heroes` property is now an *Observable* of hero arrays, rather than just a hero array.
The `*ngFor` can't do anything with !{_an} `!{_Observable}` until you route it through the `async` pipe (`AsyncPipe`). The `*ngFor` can't do anything with an `Observable` until you route it through the `async` pipe (`AsyncPipe`).
The `async` pipe subscribes to the `!{_Observable}` and produces the !{_array} of heroes to `*ngFor`. The `async` pipe subscribes to the `Observable` and produces the array of heroes to `*ngFor`.
Create the `HeroSearchComponent` class and metadata. Create the `HeroSearchComponent` class and metadata.
<code-example path="toh-6/src/app/hero-search.component.ts">
<code-example path="toh-pt6/src/app/hero-search.component.ts">
</code-example> </code-example>
#### Search terms #### Search terms
Focus on `!{_priv}searchTerms`: Focus on the `searchTerms`:
<code-example path="toh-6/src/app/hero-search.component.ts" region="searchTerms">
<code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (searchTerms)" region="searchTerms">
</code-example> </code-example>
A `Subject` is a producer of an _observable_ event stream; A `Subject` is a producer of an _observable_ event stream;
`searchTerms` produces an `Observable` of strings, the filter criteria for the name search. `searchTerms` produces an `Observable` of strings, the filter criteria for the name search.
@ -518,26 +481,24 @@ Each call to `search()` puts a new string into this subject's _observable_ strea
<a id="ngoninit"></a> <a id="ngoninit"></a>
#### Initialize the *heroes* property (*ngOnInit*) #### Initialize the *heroes* property (*ngOnInit*)
<span if-docs="ts">A `Subject` is also an `Observable`.</span> A `Subject` is also an `Observable`.
You can turn the stream You can turn the stream
of search terms into a stream of `Hero` !{_array}s and assign the result to the `heroes` property. of search terms into a stream of `Hero` arrays and assign the result to the `heroes` property.
<code-example path="toh-6/src/app/hero-search.component.ts" region="search">
<code-example path="toh-pt6/src/app/hero-search.component.ts" linenums="false" title="src/app/hero-search.component.ts (search)" region="search">
</code-example> </code-example>
Passing every user keystroke directly to the `HeroSearchService` would create an excessive amount of HTTP requests, Passing every user keystroke directly to the `HeroSearchService` would create an excessive amount of HTTP requests,
taxing server resources and burning through the cellular network data plan. taxing server resources and burning through the cellular network data plan.
Instead, you can chain `Observable` operators that reduce the request flow to the string `Observable`. Instead, you can chain `Observable` operators that reduce the request flow to the string `Observable`.
You'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how: You'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds * `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
before passing along the latest string. You'll never make requests more frequently than 300ms. before passing along the latest string. You'll never make requests more frequently than 300ms.
* `distinctUntilChanged()` ensures that a request is sent only if the filter text changed. * `distinctUntilChanged` ensures that a request is sent only if the filter text changed.
* `switchMap()` calls the search service for each search term that makes it through `debounceTime()` and `distinctUntilChanged()`. * `switchMap()` calls the search service for each search term that makes it through `debounce` and `distinctUntilChanged`.
It cancels and discards previous search observables, returning only the latest search service observable. It cancels and discards previous search observables, returning only the latest search service observable.
@ -577,8 +538,7 @@ When you need more RxJS features, extend `Observable` by *importing* the librar
Here are all the RxJS imports that _this_ component needs: Here are all the RxJS imports that _this_ component needs:
<code-example path="toh-6/src/app/hero-search.component.ts" region="rxjs-imports" linenums="false">
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" linenums="false">
</code-example> </code-example>
@ -593,18 +553,16 @@ loads and executes the library's script file which, in turn, adds the operator t
Add the hero search HTML element to the bottom of the `DashboardComponent` template. Add the hero search HTML element to the bottom of the `DashboardComponent` template.
<code-example path="toh-6/src/app/dashboard.component.html" linenums="false">
<code-example path="toh-pt6/src/app/dashboard.component.html" linenums="false">
</code-example> </code-example>
Finally, import `HeroSearchComponent` from Finally, import `HeroSearchComponent` from
<span ngio-ex>hero-search.component.ts</span> <code>hero-search.component.ts</code>
and add it to the `!{_declarations}` !{_array}. and add it to the `declarations` array.
<code-example path="toh-6/src/app/app.module.ts" region="search">
<code-example path="toh-pt6/declFile" linenums="false" title="declFile (search)" region="search">
</code-example> </code-example>
@ -623,189 +581,128 @@ Review the sample source code in the <live-example></live-example> for this page
Verify that you have the following structure: Verify that you have the following structure:
<aio-filetree> <aio-filetree>
<aio-folder> <aio-folder>
angular-tour-of-heroes angular-tour-of-heroes
<aio-folder> <aio-folder>
src src
<aio-folder> <aio-folder>
app app
<aio-file> <aio-file>
app.component.ts app.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app.component.css app.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
app.module.ts app.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
app-routing.module.ts app-routing.module.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.css dashboard.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.html dashboard.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
dashboard.component.ts dashboard.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.ts hero.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.css hero-detail.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.html hero-detail.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-detail.component.ts hero-detail.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.html (new) hero-search.component.html (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.css (new) hero-search.component.css (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.component.ts (new) hero-search.component.ts (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero-search.service.ts (new) hero-search.service.ts (new)
</aio-file> </aio-file>
<aio-file> <aio-file>
hero.service.ts hero.service.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.css heroes.component.css
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.html heroes.component.html
</aio-file> </aio-file>
<aio-file> <aio-file>
heroes.component.ts heroes.component.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
in-memory-data.service.ts (new) in-memory-data.service.ts (new)
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
main.ts main.ts
</aio-file> </aio-file>
<aio-file> <aio-file>
index.html index.html
</aio-file> </aio-file>
<aio-file> <aio-file>
styles.css styles.css
</aio-file> </aio-file>
<aio-file> <aio-file>
systemjs.config.js systemjs.config.js
</aio-file> </aio-file>
<aio-file> <aio-file>
tsconfig.json tsconfig.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
<aio-file> <aio-file>
node_modules ... node_modules ...
</aio-file> </aio-file>
<aio-file> <aio-file>
package.json package.json
</aio-file> </aio-file>
</aio-folder> </aio-folder>
</aio-filetree> </aio-filetree>
@ -817,100 +714,71 @@ You're at the end of your journey, and you've accomplished a lot.
- You extended `HeroService` to support `post()`, `put()`, and `delete()` methods. - You extended `HeroService` to support `post()`, `put()`, and `delete()` methods.
- You updated the components to allow adding, editing, and deleting of heroes. - You updated the components to allow adding, editing, and deleting of heroes.
- You configured an in-memory web API. - You configured an in-memory web API.
- You learned how to use !{_Observable}s. - You learned how to use Observables.
Here are the files you added or changed in this page. Here are the files you added or changed in this page.
<code-tabs> <code-tabs>
<code-pane title="app.comp...ts" path="toh-6/src/app/app.component.ts"> <code-pane title="app.comp...ts" path="toh-6/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="app.mod...ts" path="toh-6/src/app/app.module.ts"> <code-pane title="app.mod...ts" path="toh-6/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="heroes.comp...ts" path="toh-6/src/app/heroes.component.ts"> <code-pane title="heroes.comp...ts" path="toh-6/src/app/heroes.component.ts">
</code-pane> </code-pane>
<code-pane title="heroes.comp...html" path="toh-6/src/app/heroes.component.html"> <code-pane title="heroes.comp...html" path="toh-6/src/app/heroes.component.html">
</code-pane> </code-pane>
<code-pane title="heroes.comp...css" path="toh-6/src/app/heroes.component.css"> <code-pane title="heroes.comp...css" path="toh-6/src/app/heroes.component.css">
</code-pane> </code-pane>
<code-pane title="hero-detail.comp...ts" path="toh-6/src/app/hero-detail.component.ts"> <code-pane title="hero-detail.comp...ts" path="toh-6/src/app/hero-detail.component.ts">
</code-pane> </code-pane>
<code-pane title="hero-detail.comp...html" path="toh-6/src/app/hero-detail.component.html"> <code-pane title="hero-detail.comp...html" path="toh-6/src/app/hero-detail.component.html">
</code-pane> </code-pane>
<code-pane title="hero.service.ts" path="toh-6/src/app/hero.service.ts"> <code-pane title="hero.service.ts" path="toh-6/src/app/hero.service.ts">
</code-pane> </code-pane>
<code-pane title="in-memory-data.service.ts" path="toh-6/src/app/in-memory-data.service.ts"> <code-pane title="in-memory-data.service.ts" path="toh-6/src/app/in-memory-data.service.ts">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
<code-tabs> <code-tabs>
<code-pane title="hero-search.service.ts" path="toh-6/src/app/hero-search.service.ts"> <code-pane title="hero-search.service.ts" path="toh-6/src/app/hero-search.service.ts">
</code-pane> </code-pane>
<code-pane title="hero-search.component.ts" path="toh-6/src/app/hero-search.component.ts"> <code-pane title="hero-search.component.ts" path="toh-6/src/app/hero-search.component.ts">
</code-pane> </code-pane>
<code-pane title="hero-search.component.html" path="toh-6/src/app/hero-search.component.html"> <code-pane title="hero-search.component.html" path="toh-6/src/app/hero-search.component.html">
</code-pane> </code-pane>
<code-pane title="hero-search.component.css" path="toh-6/src/app/hero-search.component.css"> <code-pane title="hero-search.component.css" path="toh-6/src/app/hero-search.component.css">
</code-pane> </code-pane>
</code-tabs> </code-tabs>