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>
@ -516,14 +488,14 @@ Open a _different_ terminal window and enter `npm start`.
That compiles the app with JIT and launches the server. That compiles the app with JIT and launches the server.
The server loads `index.html` which is still the AOT version, which you can confirm in the browser console. The server loads `index.html` which is still the AOT version, which you can confirm in the browser console.
Change the address bar to `index-jit.html` and it loads the JIT version. Change the address bar to `index-jit.html` and it loads the JIT version.
This is also evident in the browser console. This is also evident in the browser console.
Develop as usual. Develop as usual.
The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser. The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser.
To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`. To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`.
When it finishes, go back to the browser and use the back button to When it finishes, go back to the browser and use the back button to
return to the AOT version in the default `index.html`. return to the AOT version in the default `index.html`.
Now you can develop JIT and AOT, side-by-side. Now you can develop JIT and AOT, side-by-side.
@ -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

@ -5,7 +5,7 @@ Dependency Injection
Techniques for Dependency Injection. Techniques for Dependency Injection.
@description @description
Dependency Injection is a powerful pattern for managing code dependencies. Dependency Injection is a powerful pattern for managing code dependencies.
In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular. In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular.
<a id="toc"></a>## Table of contents <a id="toc"></a>## Table of contents
@ -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)
@ -45,22 +48,21 @@ In this cookbook we will explore many of the features of Dependency Injection (D
[Break circularities with a forward class reference (*forwardRef*)](guide/cb-dependency-injection#forwardref) [Break circularities with a forward class reference (*forwardRef*)](guide/cb-dependency-injection#forwardref)
**See the <live-example name="cb-dependency-injection"></live-example>** **See the <live-example name="cb-dependency-injection"></live-example>**
of the code supporting this cookbook. of the code supporting this cookbook.
<a id="app-wide-dependencies"></a>## Application-wide dependencies <a id="app-wide-dependencies"></a>## Application-wide dependencies
Register providers for dependencies used throughout the application in the root application component, `AppComponent`. Register providers for dependencies used throughout the application in the root application component, `AppComponent`.
In the following example, we import and register several services In the following example, we import and register several services
(the `LoggerService`, `UserContext`, and the `UserService`) (the `LoggerService`, `UserContext`, and the `UserService`)
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>
All of these services are implemented as classes. All of these services are implemented as classes.
Service classes can act as their own providers which is why listing them in the `providers` array Service classes can act as their own providers which is why listing them in the `providers` array
is all the registration we need. is all the registration we need.
@ -72,17 +74,15 @@ 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>
@ -95,11 +95,10 @@ We do this when (a) we expect the service to be injectable everywhere
or (b) we must configure another application global service _before it starts_. or (b) we must configure another application global service _before it starts_.
We see an example of the second case here, where we configure the Component Router with a non-default We see an example of the second case here, where we configure the Component Router with a non-default
[location strategy](guide/router) by listing its provider [location strategy](guide/router) by listing its provider
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>
@ -120,40 +119,37 @@ Sometimes a service depends on other services ... which may depend on yet other
Resolving these nested dependencies in the correct order is also the framework's job. Resolving these nested dependencies in the correct order is also the framework's job.
At each step, the consumer of dependencies simply declares what it requires in its constructor and the framework takes over. At each step, the consumer of dependencies simply declares what it requires in its constructor and the framework takes over.
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>
The `UserContext` in turn has dependencies on both the `LoggerService` (again) and The `UserContext` in turn has dependencies on both the `LoggerService` (again) and
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>
When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and
starts to create the `UserContextService`. starts to create the `UserContextService`.
The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create. The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create.
The `UserService` has no dependencies so the dependency injection framework can just `new` one into existence. The `UserService` has no dependencies so the dependency injection framework can just `new` one into existence.
The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this.
The author simply declared what was needed in the constructor (`LoggerService` and `UserContextService`) and the framework did the rest. The author simply declared what was needed in the constructor (`LoggerService` and `UserContextService`) and the framework did the rest.
Once all the dependencies are in place, the `AppComponent` displays the user information: Once all the dependencies are in place, the `AppComponent` displays the user information:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User"> </img> <img src="assets/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User"> </img>
</figure> </figure>
### *@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">
@ -163,7 +159,7 @@ That decorator makes it possible for Angular to identify the types of its two de
Technically, the `@Injectable()`decorator is only _required_ for a service class that has _its own dependencies_. Technically, the `@Injectable()`decorator is only _required_ for a service class that has _its own dependencies_.
The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()` The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()`
and the generated code would be slightly smaller. and the generated code would be slightly smaller.
But the service would break the moment we gave it a dependency and we'd have to go back But the service would break the moment we gave it a dependency and we'd have to go back
and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain. and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain.
@ -192,40 +188,39 @@ In Angular with TypeScript, a *single* decorator &mdash; *any* decorator &mdash;
<a id="service-scope"></a> <a id="service-scope"></a>
## Limit service scope to a component subtree ## Limit service scope to a component subtree
All injected service dependencies are singletons meaning that, All injected service dependencies are singletons meaning that,
for a given dependency injector ("injector"), there is only one instance of service. for a given dependency injector ("injector"), there is only one instance of service.
But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree. But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree.
So a particular service can be *provided* (and created) at any component level and multiple times So a particular service can be *provided* (and created) at any component level and multiple times
if provided in multiple components. if provided in multiple components.
By default, a service dependency provided in one component is visible to all of its child components and By default, a service dependency provided in one component is visible to all of its child components and
Angular injects the same service instance into all child components that ask for that service. Angular injects the same service instance into all child components that ask for that service.
Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application. Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application.
That isn't always desirable. That isn't always desirable.
Sometimes we want to restrict service availability to a particular region of the application. Sometimes we want to restrict service availability to a particular region of the application.
We can limit the scope of an injected service to a *branch* of the application hierarchy We can limit the scope of an injected service to a *branch* of the application hierarchy
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>
When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService` When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService`
that is visible only to the component and its children (if any). that is visible only to the component and its children (if any).
We could also provide the `HeroService` to a *different* component elsewhere in the application. We could also provide the `HeroService` to a *different* component elsewhere in the application.
That would result in a *different* instance of the service, living in a *different* injector. That would result in a *different* instance of the service, living in a *different* injector.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code,
including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`.
Each of these components has its own `HeroService` instance managing its own independent collection of heroes. Each of these components has its own `HeroService` instance managing its own independent collection of heroes.
@ -248,48 +243,45 @@ ever need to build their applications. It doesn't always have to be more complic
Sometimes we want multiple instances of a service at *the same level of the component hierarchy*. Sometimes we want multiple instances of a service at *the same level of the component hierarchy*.
A good example is a service that holds state for its companion component instance. A good example is a service that holds state for its companion component instance.
We need a separate instance of the service for each component. We need a separate instance of the service for each component.
Each service has its own work-state, isolated from the service-and-state of a different component. Each service has its own work-state, isolated from the service-and-state of a different component.
We call this *sandboxing* because each service and component instance has its own sandbox to play in. We call this *sandboxing* because each service and component instance has its own sandbox to play in.
<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>
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>
Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`. Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`.
They'd be competing with each other to determine which hero to cache. 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>
The parent `HeroBiosComponent` binds a value to the `heroId`. The parent `HeroBiosComponent` binds a value to the `heroId`.
The `ngOnInit` pass that `id` to the service which fetches and caches the hero. The `ngOnInit` pass that `id` to the service which fetches and caches the hero.
The getter for the `hero` property pulls the cached hero from the service. The getter for the `hero` property pulls the cached hero from the service.
And the template displays this data-bound property. And the template displays this data-bound property.
Find this example in <live-example name="cb-dependency-injection">live code</live-example> Find this example in <live-example name="cb-dependency-injection">live code</live-example>
and confirm that the three `HeroBioComponent` instances have their own cached hero data. and confirm that the three `HeroBioComponent` instances have their own cached hero data.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img> <img src="assets/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios"> </img>
</figure> </figure>
@ -300,45 +292,42 @@ and confirm that the three `HeroBioComponent` instances have their own cached he
{@a qualify-dependency-lookup} {@a qualify-dependency-lookup}
## Qualify dependency lookup with *@Optional* and *@Host* ## Qualify dependency lookup with *@Optional* and *@Host*
We learned that dependencies can be registered at any level in the component hierarchy. We learned that dependencies can be registered at any level in the component hierarchy.
When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree
until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk. until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk.
We *want* this behavior most of the time. We *want* this behavior most of the time.
But sometimes we need to limit the search and/or accommodate a missing dependency. But sometimes we need to limit the search and/or accommodate a missing dependency.
We can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators, We can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators,
used individually or together. used individually or together.
The `@Optional` decorator tells Angular to continue when it can't find the dependency. The `@Optional` decorator tells Angular to continue when it can't find the dependency.
Angular sets the injection parameter to `null` instead. Angular sets the injection parameter to `null` instead.
The `@Host` decorator stops the upward search at the *host component*. The `@Host` decorator stops the upward search at the *host component*.
The host component is typically the component requesting the dependency. The host component is typically the component requesting the dependency.
But when this component is projected into a *parent* component, that parent component becomes the host. But when this component is projected into a *parent* component, that parent component becomes the host.
We look at this second, more interesting case in our next example. 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>
We've inserted a `<hero-contact>` element between the `<hero-bio>` tags. 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>
@ -346,24 +335,22 @@ placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description: It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact"> </img> <img src="assets/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact"> </img>
</figure> </figure>
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section:
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="component">
</code-example> </code-example>
Focus on the constructor parameters Focus on the constructor parameters
<code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false"> <code-example path="cb-dependency-injection/src/app/hero-contact.component.ts" region="ctor-params" linenums="false">
</code-example> </code-example>
The `@Host()` function decorating the `heroCache` property ensures that The `@Host()` function decorating the `heroCache` property ensures that
we get a reference to the cache service from the parent `HeroBioComponent`. we get a reference to the cache service from the parent `HeroBioComponent`.
Angular throws if the parent lacks that service, even if a component higher in the component tree happens to have that service. Angular throws if the parent lacks that service, even if a component higher in the component tree happens to have that service.
@ -387,28 +374,27 @@ Here's the `HeroBiosAndContactsComponent` in action.
<img src="assets/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img> <img src="assets/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"> </img>
</figure> </figure>
If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree
until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates
with the gratuitous "!!!", indicating that the logger was found. with the gratuitous "!!!", indicating that the logger was found.
<figure class='image-display'> <figure class='image-display'>
<img src="assets/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"> </img> <img src="assets/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"> </img>
</figure> </figure>
On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`, On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`,
the application fails for lack of the required logger at the host component level. the application fails for lack of the required logger at the host component level.
<br> <br>
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` `EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)`
<a id="component-element"></a>## Inject the component's element <a id="component-element"></a>## Inject the component's element
On occasion we might need to access a component's corresponding DOM element. On occasion we might need to access a component's corresponding DOM element.
Although we strive to avoid it, many visual effects and 3rd party tools (such as jQuery) Although we strive to avoid it, many visual effects and 3rd party tools (such as jQuery)
require DOM access. 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>
@ -416,14 +402,13 @@ the [Attribute Directives](guide/attribute-directives) chapter.
The directive sets the background to a highlight color when the user mouses over the The directive sets the background to a highlight color when the user mouses over the
DOM element to which it is applied. DOM element to which it is applied.
Angular set the constructor's `el` parameter to the injected `ElementRef` which is Angular set the constructor's `el` parameter to the injected `ElementRef` which is
a wrapper around that DOM element. a wrapper around that DOM element.
Its `nativeElement` property exposes the DOM element for the directive to manipulate. Its `nativeElement` property exposes the DOM element for the directive to manipulate.
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>
@ -440,15 +425,14 @@ The following image shows the effect of mousing over the `<hero-bios-and-contact
In this section we learn to write providers that deliver dependent services. In this section we learn to write providers that deliver dependent services.
### Background ### Background
We get a service from a dependency injector by giving it a ***token***. We get a service from a dependency injector by giving it a ***token***.
We usually let Angular handle this transaction for us by specifying a constructor parameter and its type. We usually let Angular handle this transaction for us by specifying a constructor parameter and its type.
The parameter type serves as the injector lookup *token*. The parameter type serves as the injector lookup *token*.
Angular passes this token to the injector and assigns the result to the parameter. Angular passes this token to the injector and assigns the result to the parameter.
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>
@ -457,14 +441,14 @@ Angular asks the injector for the service associated with the `LoggerService`
and assigns the returned value to the `logger` parameter. and assigns the returned value to the `logger` parameter.
Where did the injector get that value? Where did the injector get that value?
It may already have that value in its internal container. It may already have that value in its internal container.
If it doesn't, it may be able to make one with the help of a ***provider***. If it doesn't, it may be able to make one with the help of a ***provider***.
A *provider* is a recipe for delivering a service associated with a *token*. A *provider* is a recipe for delivering a service associated with a *token*.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
If the injector doesn't have a provider for the requested *token*, it delegates the request If the injector doesn't have a provider for the requested *token*, it delegates the request
to its parent injector, where the process repeats until there are no more injectors. to its parent injector, where the process repeats until there are no more injectors.
If the search is futile, the injector throws an error ... unless the request was [optional](guide/cb-dependency-injection#optional). If the search is futile, the injector throws an error ... unless the request was [optional](guide/cb-dependency-injection#optional).
Let's return our attention to providers themselves. Let's return our attention to providers themselves.
@ -473,10 +457,9 @@ Let's return our attention to providers themselves.
A new injector has no providers. A new injector has no providers.
Angular initializes the injectors it creates with some providers it cares about. 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>
@ -495,7 +477,7 @@ It's that simple because the most common injected service is an instance of a cl
But not every dependency can be satisfied by creating a new instance of a class. But not every dependency can be satisfied by creating a new instance of a class.
We need other ways to deliver dependency values and that means we need other ways to specify a provider. We need other ways to deliver dependency values and that means we need other ways to specify a provider.
The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them. The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them.
<figure class='image-display'> <figure class='image-display'>
@ -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>
@ -518,7 +499,7 @@ It's visually simple: a few properties and the output of a logger. The code behi
The `provide` object literal takes a *token* and a *definition object*. The `provide` object literal takes a *token* and a *definition object*.
The *token* is usually a class but [it doesn't have to be](guide/cb-dependency-injection#tokens). The *token* is usually a class but [it doesn't have to be](guide/cb-dependency-injection#tokens).
The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider
should create or return the provided value. should create or return the provided value.
@ -532,10 +513,9 @@ Use this technique to provide *runtime configuration constants* such as web-site
We often use a *value provider* in a unit test to replace a production service with a fake or mock. We often use a *value provider* in a unit test to replace a production service with a fake or mock.
The `HeroOfTheMonthComponent` example has two *value providers*. 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>
@ -547,11 +527,10 @@ The `TITLE` provider token is *not a class*.
It's a special kind of provider lookup key called an [OpaqueToken](guide/cb-dependency-injection#opaquetoken). It's a special kind of provider lookup key called an [OpaqueToken](guide/cb-dependency-injection#opaquetoken).
We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function. We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function.
The value of a *value provider* must be defined *now*. We can't create the value later. The value of a *value provider* must be defined *now*. We can't create the value later.
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,13 +550,12 @@ 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>
The first provider is the *de-sugared*, expanded form of the most typical case in which the The first provider is the *de-sugared*, expanded form of the most typical case in which the
class to be created (`HeroService`) is also the provider's injection token. class to be created (`HeroService`) is also the provider's injection token.
We wrote it in this long form to de-mystify the preferred short form. We wrote it in this long form to de-mystify the preferred short form.
The second provider substitutes the `DateLoggerService` for the `LoggerService`. The second provider substitutes the `DateLoggerService` for the `LoggerService`.
@ -591,8 +569,7 @@ Components outside the tree continue to receive the original `LoggerService` ins
~~~ ~~~
The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false"> <code-example path="cb-dependency-injection/src/app/date-logger.service.ts" region="date-logger-service" linenums="false">
@ -604,11 +581,10 @@ The `DateLoggerService` inherits from `LoggerService`; it appends the current da
{@a useexisting} {@a useexisting}
#### useExisting - the *alias provider* #### useExisting - the *alias provider*
The `useExisting` provider maps one token to another. 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>
@ -630,7 +605,7 @@ The constructor's `logger` parameter is typed as `MinimalLogger` so only its two
<img src="assets/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"> </img> <img src="assets/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"> </img>
</figure> </figure>
Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService`
which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`. which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`.
The following image, which displays the logging date, confirms the point: The following image, which displays the logging date, confirms the point:
@ -647,12 +622,11 @@ 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>
Use this technique to ***create a dependency object*** Use this technique to ***create a dependency object***
with a factory function whose inputs are some ***combination of injected services and local state***. with a factory function whose inputs are some ***combination of injected services and local state***.
The *dependency object* doesn't have to be a class instance. It could be anything. The *dependency object* doesn't have to be a class instance. It could be anything.
@ -660,31 +634,30 @@ In this example, the *dependency object* is a string of the names of the runners
to the "Hero of the Month" contest. to the "Hero of the Month" contest.
The local state is the number `2`, the number of runners-up this component should show. The local state is the number `2`, the number of runners-up this component should show.
We execute `runnersUpFactory` immediately with `2`. We execute `runnersUpFactory` immediately with `2`.
The `runnersUpFactory` itself isn't the provider factory function. 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>
That returned function takes a winning `Hero` and a `HeroService` as arguments. That returned function takes a winning `Hero` and a `HeroService` as arguments.
Angular supplies these arguments from injected values identified by Angular supplies these arguments from injected values identified by
the two *tokens* in the `deps` array. the two *tokens* in the `deps` array.
The two `deps` values are *tokens* that the injector uses The two `deps` values are *tokens* that the injector uses
to provide these factory function dependencies. to provide these factory function dependencies.
After some undisclosed work, the function returns the string of names After some undisclosed work, the function returns the string of names
and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`. and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The function retrieves candidate heroes from the `HeroService`, The function retrieves candidate heroes from the `HeroService`,
takes `2` of them to be the runners-up, and returns their concatenated names. takes `2` of them to be the runners-up, and returns their concatenated names.
Look at the <live-example name="cb-dependency-injection"></live-example> Look at the <live-example name="cb-dependency-injection"></live-example>
for the full source code. for the full source code.
@ -703,20 +676,18 @@ that is also the type of the returned dependency object (what we usually call th
But the token doesn't have to be a class and even when it is a class, But the token doesn't have to be a class and even when it is a class,
it doesn't have to be the same type as the returned object. it doesn't have to be the same type as the returned object.
That's the subject of our next section. That's the subject of our next section.
<a id="class-interface"></a> <a id="class-interface"></a>
### class-interface ### class-interface
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">
@ -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>
@ -748,20 +718,19 @@ The `MinimalLogger` defines just two of the `LoggerClass` members.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
#### Why *MinimalLogger* is a class and not an interface #### Why *MinimalLogger* is a class and not an interface
We can't use an interface as a provider token because We can't use an interface as a provider token because
interfaces are not JavaScript objects. interfaces are not JavaScript objects.
They exist only in the TypeScript design space. They exist only in the TypeScript design space.
They disappear after the code is transpiled to JavaScript. They disappear after the code is transpiled to JavaScript.
A provider token must be a real JavaScript object of some kind: A provider token must be a real JavaScript object of some kind:
a function, an object, a string ... a class. a function, an object, a string ... a class.
Using a class as an interface gives us the characteristics of an interface in a JavaScript object. Using a class as an interface gives us the characteristics of an interface in a JavaScript object.
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>
@ -776,26 +745,24 @@ It never grows larger no matter how many members we add *as long as they are typ
{@a opaque-token} {@a opaque-token}
### OpaqueToken ### OpaqueToken
Dependency objects can be simple values like dates, numbers and strings or Dependency objects can be simple values like dates, numbers and strings or
shapeless objects like arrays and functions. shapeless objects like arrays and functions.
Such objects don't have application interfaces and therefore aren't well represented by a class. Such objects don't have application interfaces and therefore aren't well represented by a class.
They're better represented by a token that is both unique and symbolic, They're better represented by a token that is both unique and symbolic,
a JavaScript object that has a friendly name but won't conflict with a JavaScript object that has a friendly name but won't conflict with
another token that happens to have the same name. another token that happens to have the same name.
The `OpaqueToken` has these characteristics. 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>
@ -806,11 +773,11 @@ We created the `TITLE` token like this:
## Inject into a derived class ## Inject into a derived class
We must take care when writing a component that inherits from another component. We must take care when writing a component that inherits from another component.
If the base component has injected dependencies, If the base component has injected dependencies,
we must re-provide and re-inject them in the derived class we must re-provide and re-inject them in the derived class
and then pass them down to the base class through the constructor. and then pass them down to the base class through the constructor.
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
to display a *sorted* list of heroes. to display a *sorted* list of heroes.
@ -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>
@ -847,23 +813,22 @@ The `SortedHeroesComponent` lets the base class fetch the heroes.
(we said it was contrived). (we said it was contrived).
Unfortunately, Angular cannot inject the `HeroService` directly into the base class. Unfortunately, Angular cannot inject the `HeroService` directly into the base class.
We must provide the `HeroService` again for *this* component, 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>
Now take note of the `afterGetHeroes` method. Now take note of the `afterGetHeroes` method.
Our first instinct was to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there. Our first instinct was to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there.
But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit`
so we'd be sorting the heroes array *before they arrived*. That produces a nasty error. so we'd be sorting the heroes array *before they arrived*. That produces a nasty error.
Overriding the base class's `afterGetHeroes` method solves the problem Overriding the base class's `afterGetHeroes` method solves the problem
These complications argue for *avoiding component inheritance*. These complications argue for *avoiding component inheritance*.
{@a find-parent} {@a find-parent}
@ -874,10 +839,10 @@ Application components often need to share information.
We prefer the more loosely coupled techniques such as data binding and service sharing. We prefer the more loosely coupled techniques such as data binding and service sharing.
But sometimes it makes sense for one component to have a direct reference to another component But sometimes it makes sense for one component to have a direct reference to another component
perhaps to access values or call methods on that component. perhaps to access values or call methods on that component.
Obtaining a component reference is a bit tricky in Angular. Obtaining a component reference is a bit tricky in Angular.
Although an Angular application is a tree of components, Although an Angular application is a tree of components,
there is no public API for inspecting and traversing that tree. there is no public API for inspecting and traversing that tree.
There is an API for acquiring a child reference There is an API for acquiring a child reference
(checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`). (checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`).
@ -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>
@ -922,7 +885,7 @@ What if we do *not* know the concrete parent component class?
A re-usable component might be a child of multiple components. A re-usable component might be a child of multiple components.
Imagine a component for rendering breaking news about a financial instrument. Imagine a component for rendering breaking news about a financial instrument.
For sound (cough) business reasons, this news component makes frequent calls For sound (cough) business reasons, this news component makes frequent calls
directly into its parent instrument as changing market data stream by. directly into its parent instrument as changing market data stream by.
The app probably defines more than a dozen financial instrument components. The app probably defines more than a dozen financial instrument components.
@ -938,25 +901,23 @@ which doesn't support interfaces. There's no artifact we could look for.
~~~ ~~~
We're not claiming this is good design. We're not claiming this is good design.
We are asking *can a component inject its parent via the parent's base class*? 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>
Unfortunately, this does not work. Unfortunately, this does not work.
The <live-example name="cb-dependency-injection"></live-example> The <live-example name="cb-dependency-injection"></live-example>
confirms that the `alex` parameter is null. confirms that the `alex` parameter is null.
*We cannot inject a parent by its base class.* *We cannot inject a parent by its base class.*
@ -966,9 +927,9 @@ confirms that the `alex` parameter is null.
We can find a parent component with a [class-interface](guide/cb-dependency-injection#class-interface). We can find a parent component with a [class-interface](guide/cb-dependency-injection#class-interface).
The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token. The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token.
Recall that Angular always adds a component instance to its own injector; Recall that Angular always adds a component instance to its own injector;
that's why we could inject *Alex* into *Cathy* [earlier](guide/cb-dependency-injection#known-parent). that's why we could inject *Alex* into *Cathy* [earlier](guide/cb-dependency-injection#known-parent).
We write an [*alias provider*](guide/cb-dependency-injection#useexisting) &mdash; a `provide` object literal with a `useExisting` definition &mdash; We write an [*alias provider*](guide/cb-dependency-injection#useexisting) &mdash; a `provide` object literal with a `useExisting` definition &mdash;
@ -978,17 +939,15 @@ 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>
[Parent](guide/cb-dependency-injection#parent-token) is the provider's *class-interface* token. [Parent](guide/cb-dependency-injection#parent-token) is the provider's *class-interface* token.
The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself. The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself.
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: *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>
@ -1004,7 +963,7 @@ Here's *Alex* and family in action:
{@a parent-tree} {@a parent-tree}
### Find the parent in a tree of parents ### Find the parent in a tree of parents
Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*.
Both *Alice* and *Barry* implement the `Parent` *class-interface*. Both *Alice* and *Barry* implement the `Parent` *class-interface*.
*Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*. *Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*.
@ -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,27 +981,22 @@ 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.
`@SkipSelf` is essential for two reasons: `@SkipSelf` is essential for two reasons:
1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, 1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself,
which *is* what parent means. which *is* what parent means.
@ -1066,27 +1019,24 @@ 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>
The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*., The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*.,
The `name` property is the only member of a parent component that a child component can call. The `name` property is the only member of a parent component that a child component can call.
Such a narrowing interface helps decouple the child component class from its parent components. Such a narrowing interface helps decouple the child component class from its parent components.
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>
Doing so adds clarity to the code. But it's not technically necessary. 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>
@ -1095,8 +1045,8 @@ its class signature doesn't mention `Parent`:
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The `AlexComponent` *should* implement `Parent` as a matter of proper style. The `AlexComponent` *should* implement `Parent` as a matter of proper style.
It doesn't in this example *only* to demonstrate that the code will compile and run without the interface It doesn't in this example *only* to demonstrate that the code will compile and run without the interface
~~~ ~~~
@ -1106,24 +1056,21 @@ It doesn't in this example *only* to demonstrate that the code will compile and
{@a provideparent} {@a provideparent}
### A *provideParent* helper function ### A *provideParent* helper function
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>
@ -1155,21 +1100,20 @@ The order of class declaration matters in TypeScript.
We can't refer directly to a class until it's been defined. We can't refer directly to a class until it's been defined.
This isn't usually a problem, especially if we adhere to the recommended *one class per file* rule. This isn't usually a problem, especially if we adhere to the recommended *one class per file* rule.
But sometimes circular references are unavoidable. But sometimes circular references are unavoidable.
We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'. We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first. One of them has to be defined first.
The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later. The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later.
The *Parent Finder* sample is full of circular class references that are impossible to break. The *Parent Finder* sample is full of circular class references that are impossible to break.
We face this dilemma when a class makes *a reference to itself* We face this dilemma when a class makes *a reference to itself*
as does the `AlexComponent` in its `providers` array. as does the `AlexComponent` in its `providers` array.
The `providers` array is a property of the `@Component` decorator function which must The `providers` array is a property of the `@Component` decorator function which must
appear *above* the class definition. 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)
@ -23,7 +24,7 @@ This page describes tools and techniques for deploy and optimize your Angular ap
* [Enable production mode](guide/deployment#enable-prod-mode) * [Enable production mode](guide/deployment#enable-prod-mode)
* [Lazy loading](guide/deployment#lazy-loading) * [Lazy loading](guide/deployment#lazy-loading)
* [Server configuration](guide/deployment#server-configuration) * [Server configuration](guide/deployment#server-configuration)
* [Routed apps must fallback to `index.html`](guide/deployment#fallback) * [Routed apps must fallback to `index.html`](guide/deployment#fallback)
* [CORS: requesting services from a different server](guide/deployment#cors) * [CORS: requesting services from a different server](guide/deployment#cors)
@ -36,8 +37,8 @@ The techniques progress from _easy but suboptimal_ to _more optimal and more inv
* The [simple way](guide/deployment#dev-deploy "Simplest deployment possible") is to copy the development environment to the server. * The [simple way](guide/deployment#dev-deploy "Simplest deployment possible") is to copy the development environment to the server.
* [_Ahead of Time_ compilation (AOT)](guide/deployment#aot "AOT Compilation") is the first of * [_Ahead of Time_ compilation (AOT)](guide/deployment#aot "AOT Compilation") is the first of
[several optimization strategies](guide/deployment#optimize). [several optimization strategies](guide/deployment#optimize).
You'll also want to read the [detailed instructions in the AOT Cookbook](cookbook/aot-compiler). You'll also want to read the [detailed instructions in the AOT Cookbook](cookbook/aot-compiler).
* [Webpack](guide/deployment#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT. * [Webpack](guide/deployment#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
@ -53,20 +54,20 @@ server-side changes that may be necessary, _no matter how you deploy the applica
{@a dev-deploy} {@a dev-deploy}
## Simplest deployment possible ## Simplest deployment possible
The simplest way to deploy the app is to publish it to a web server The simplest way to deploy the app is to publish it to a web server
directly out of the development environment. directly out of the development environment.
It's already running locally. You'll just copy it, almost _as is_, It's already running locally. You'll just copy it, almost _as is_,
to a non-local server that others can reach. to a non-local server that others can reach.
1. Copy _everything_ (or [_almost_ everything](guide/deployment#node-modules "Loading npm packages from the web")) 1. Copy _everything_ (or [_almost_ everything](guide/deployment#node-modules "Loading npm packages from the web"))
from the local project folder to a folder on the server. from the local project folder to a folder on the server.
1. If you're serving the app out of a subfolder, 1. If you're serving the app out of a subfolder,
edit a version of `index.html` to set the `<base href>` appropriately. edit a version of `index.html` to set the `<base href>` appropriately.
For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to
`<base href="/my/app/">`. `<base href="/my/app/">`.
Otherwise, leave it alone. Otherwise, leave it alone.
[More on this below](guide/deployment#base-tag). [More on this below](guide/deployment#base-tag).
@ -94,12 +95,12 @@ Be sure to read about [optimizing for production](guide/deployment#optimize "Opt
{@a node-modules} {@a node-modules}
### Load npm package files from the web (SystemJS) ### Load npm package files from the web (SystemJS)
The `node_modules` folder of _npm packages_ contains much more code The `node_modules` folder of _npm packages_ contains much more code
than is needed to actually run your app in the browser. than is needed to actually run your app in the browser.
The `node_modules` for the Quickstart installation is typically 20,500+ files and 180+ MB. The `node_modules` for the Quickstart installation is typically 20,500+ files and 180+ MB.
The application itself requires a tiny fraction of that to run. The application itself requires a tiny fraction of that to run.
It takes a long time to upload all of that useless bulk and It takes a long time to upload all of that useless bulk and
users will wait unnecessarily while library files download piecemeal. users will wait unnecessarily while library files download piecemeal.
Load the few files you need from the web instead. Load the few files you need from the web instead.
@ -108,21 +109,19 @@ 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>
(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>
(3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder. (3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder.
This alternative version configures _SystemJS_ to load _UMD_ versions of Angular This alternative version configures _SystemJS_ to load _UMD_ versions of Angular
(and other third-party packages) from the web. (and other third-party packages) from the web.
Modify `systemjs.config.server.js` as necessary to stay in sync with changes Modify `systemjs.config.server.js` as necessary to stay in sync with changes
@ -131,21 +130,20 @@ 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>
In the standard SystemJS config, the `npm` path points to the `node_modules/`. In the standard SystemJS config, the `npm` path points to the `node_modules/`.
In this server config, it points to In this server config, it points to
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>, <a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
a site that hosts _npm packages_, a site that hosts _npm packages_,
and loads them from the web directly. and loads them from the web directly.
There are other service providers that do the same thing. There are other service providers that do the same thing.
If you are unwilling or unable to load packages from the open web, If you are unwilling or unable to load packages from the open web,
the inventory in `systemjs.config.server.js` identifies the files and folders that the inventory in `systemjs.config.server.js` identifies the files and folders that
you would copy to a library folder on the server. you would copy to a library folder on the server.
Then change the config's `'npm'` path to point to that folder. Then change the config's `'npm'` path to point to that folder.
### Practice with an example ### Practice with an example
@ -153,65 +151,50 @@ 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.
1. Run it with `npm start` as you would any project. 1. Run it with `npm start` as you would any project.
1. Inspect the network traffic in the browser developer tools. 1. Inspect the network traffic in the browser developer tools.
Notice that it loads all packages from the web. Notice that it loads all packages from the web.
You could delete the `node_modules` folder and the app would still run You could delete the `node_modules` folder and the app would still run
(although you wouldn't be able to recompile or launch `lite-server` (although you wouldn't be able to recompile or launch `lite-server`
until you restored it). until you restored it).
@ -232,7 +215,7 @@ Each small file download can spend more time communicating with the server than
Development files are full of comments and whitespace for easy reading and debugging. Development files are full of comments and whitespace for easy reading and debugging.
The browser downloads entire libraries, instead of just the parts the app needs. The browser downloads entire libraries, instead of just the parts the app needs.
The volume of code passed from server to client (the "payload") The volume of code passed from server to client (the "payload")
can be significantly larger than is strictly necessary to execute the application. can be significantly larger than is strictly necessary to execute the application.
The many requests and large payloads mean The many requests and large payloads mean
@ -244,7 +227,7 @@ Does it matter? That depends upon business and technical factors you must evalua
If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses. If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses.
- Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates. - Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
- Bundling: concatenates modules into a single file (bundle). - Bundling: concatenates modules into a single file (bundle).
- Inlining: pulls template html and css into the components. - Inlining: pulls template html and css into the components.
- Minification: removes excess whitespace, comments, and optional tokens. - Minification: removes excess whitespace, comments, and optional tokens.
- Uglification: rewrites code to use short, cryptic variable and function names. - Uglification: rewrites code to use short, cryptic variable and function names.
@ -252,11 +235,11 @@ If it _does_ matter, there are tools and techniques to reduce the number of requ
- Pruned libraries: drop unused libraries and pare others down to the features you need. - Pruned libraries: drop unused libraries and pare others down to the features you need.
- Performance measurement: focus on optimizations that make a measurable difference. - Performance measurement: focus on optimizations that make a measurable difference.
Each tool does something different. Each tool does something different.
They work best in combination and are mutually reinforcing. They work best in combination and are mutually reinforcing.
You can use any build system you like. You can use any build system you like.
Whatever system you choose, be sure to automate it so that Whatever system you choose, be sure to automate it so that
building for production is a single step. building for production is a single step.
@ -282,13 +265,13 @@ and using [_rollup_](guide/deployment#rollup) for bundling, minification, uglifi
<a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another <a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another
great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application. great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application.
The "[Webpack: an introduction](guide/webpack)" guide will get you started The "[Webpack: an introduction](guide/webpack)" guide will get you started
using webpack with Angular. using webpack with Angular.
Consider configuring _Webpack_ with the official Consider configuring _Webpack_ with the official
<a href="https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack" target="_blank" title="Ahead-of-Time Webpack Plugin"> <a href="https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack" target="_blank" title="Ahead-of-Time Webpack Plugin">
Angular Ahead-of-Time Webpack Plugin</a>. Angular Ahead-of-Time Webpack Plugin</a>.
This plugin transpiles the TypeScript application code, This plugin transpiles the TypeScript application code,
bundles lazy loaded `NgModules` separately, bundles lazy loaded `NgModules` separately,
and performs AOT compilation &mdash; without any changes to the source code. and performs AOT compilation &mdash; without any changes to the source code.
@ -303,7 +286,7 @@ _Tree shaking_ is a _dead code elimination_ technique that removes entire export
If a library exports something that the application doesn't import, a tree shaking tool removes it from the code base. If a library exports something that the application doesn't import, a tree shaking tool removes it from the code base.
Tree shaking was popularized by Tree shaking was popularized by
<a href="http://rollupjs.org/" target="_blank" title="Rollup">Rollup</a>, a popular tool with an ecosystem of <a href="http://rollupjs.org/" target="_blank" title="Rollup">Rollup</a>, a popular tool with an ecosystem of
plugins for bundling, minification, and uglification. plugins for bundling, minification, and uglification.
Learn more about tree shaking and dead code elmination in Learn more about tree shaking and dead code elmination in
<a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" target="_blank" title="Tree-shaking and Dead Code Elimination"> <a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" target="_blank" title="Tree-shaking and Dead Code Elimination">
@ -328,15 +311,15 @@ Other libraries let you import features _a la carte_.
You can make better decisions about what to optimize and how when you have a clear and accurate understanding of You can make better decisions about what to optimize and how when you have a clear and accurate understanding of
what's making the application slow. what's making the application slow.
The cause may not be what you think it is. The cause may not be what you think it is.
You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower. You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower.
You should measure the app's actual behavior when running in the environments that are important to you. You should measure the app's actual behavior when running in the environments that are important to you.
The The
<a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" title="Chrome DevTools Network Performance"> <a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" title="Chrome DevTools Network Performance">
Chrome DevTools Network Performance page</a> is a good place to start learning about measuring performance. Chrome DevTools Network Performance page</a> is a good place to start learning about measuring performance.
The [WebPageTest](https://www.webpagetest.org/) tool is another good choice The [WebPageTest](https://www.webpagetest.org/) tool is another good choice
that can also help verify that your deployment was successful. that can also help verify that your deployment was successful.
@ -352,7 +335,7 @@ Angular configuration can make the difference between whether the app launches q
The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href) The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets. specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg` For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
into a server request for `my/app/some/place/foo.jpg`. into a server request for `my/app/some/place/foo.jpg`.
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files. During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
@ -363,11 +346,11 @@ See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let) alternative.
~~~ ~~~
In development, you typically start the server in the folder that holds `index.html`. In development, you typically start the server in the folder that holds `index.html`.
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app. That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
But on the shared or production server, you might serve the app from a subfolder. But on the shared or production server, you might serve the app from a subfolder.
For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`, For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`,
the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`. the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`.
When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors
@ -387,8 +370,7 @@ console:
Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles. Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles.
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">
@ -403,7 +385,7 @@ To enable [production mode](api/core/index/enableProdMode-function) when running
You can dramatically reduce launch time by only loading the application modules that You can dramatically reduce launch time by only loading the application modules that
absolutely must be present when the app starts. absolutely must be present when the app starts.
Configure the Angular Router to defer loading of all other modules (and their associated code), either by Configure the Angular Router to defer loading of all other modules (and their associated code), either by
[waiting until the app has launched](guide/router) [waiting until the app has launched](guide/router)
or by [_lazy loading_](guide/router) or by [_lazy loading_](guide/router)
them on demand. them on demand.
@ -414,14 +396,14 @@ It's a common mistake.
You've arranged to lazy load a module. You've arranged to lazy load a module.
But you unintentionally import it, with a JavaScript `import` statement, But you unintentionally import it, with a JavaScript `import` statement,
in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`. in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`.
If you do that, the module will be loaded immediately. If you do that, the module will be loaded immediately.
The bundling configuration must take lazy loading into consideration. The bundling configuration must take lazy loading into consideration.
Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default. Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default.
Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules. Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules.
You have to create these bundles manually. You have to create these bundles manually.
The The
[Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack) [Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)
automatically recognizes lazy loaded `NgModules` and creates separate bundles for them. automatically recognizes lazy loaded `NgModules` and creates separate bundles for them.
@ -441,7 +423,7 @@ Angular apps are perfect candidates for serving with a simple static HTML server
You don't need a server-side engine to dynamically compose application pages because You don't need a server-side engine to dynamically compose application pages because
Angular does that on the client-side. Angular does that on the client-side.
If the app uses the Angular router, you must configure the server If the app uses the Angular router, you must configure the server
to return the application's host page (`index.html`) when asked for a file that it does not have. to return the application's host page (`index.html`) when asked for a file that it does not have.
@ -454,19 +436,19 @@ that displays the hero with `id: 42`.
There is no issue when the user navigates to that URL from within a running client. There is no issue when the user navigates to that URL from within a running client.
The Angular router interprets the URL and routes to that page and hero. The Angular router interprets the URL and routes to that page and hero.
But clicking a link in an email, entering it in the browser address bar, But clicking a link in an email, entering it in the browser address bar,
or merely refreshing the browser while on the hero detail page &mdash; or merely refreshing the browser while on the hero detail page &mdash;
all of these actions are handled by the browser itself, _outside_ the running application. all of these actions are handled by the browser itself, _outside_ the running application.
The browser makes a direct request to the server for that URL, bypassing the router. The browser makes a direct request to the server for that URL, bypassing the router.
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`. A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
configured to return `index.html` instead. configured to return `index.html` instead.
#### Fallback configuration examples #### Fallback configuration examples
There is no single configuration that works for every server. There is no single configuration that works for every server.
The following sections describe configurations for some of the most popular servers. The following sections describe configurations for some of the most popular servers.
The list is by no means exhaustive, but should provide you with a good starting point. The list is by no means exhaustive, but should provide you with a good starting point.
#### Development servers #### Development servers
@ -474,7 +456,7 @@ The list is by no means exhaustive, but should provide you with a good starting
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the - [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`. [Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the - [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
`historyApiFallback` entry in the dev server options as follows: `historyApiFallback` entry in the dev server options as follows:
@ -488,13 +470,13 @@ The list is by no means exhaustive, but should provide you with a good starting
#### Production servers #### Production servers
- [Apache](https://httpd.apache.org/): add a - [Apache](https://httpd.apache.org/): add a
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) [rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
to the `.htaccess` file as show to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/): [here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
<code-example format="."> <code-example format=".">
RewriteEngine On RewriteEngine On
# If an existing asset or directory is requested go to it as it is # If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
@ -505,7 +487,7 @@ to the `.htaccess` file as show
</code-example> </code-example>
- [NGinx](http://nginx.org/): use `try_files`, as described in - [NGinx](http://nginx.org/): use `try_files`, as described in
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps), [Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
modified to serve `index.html`: modified to serve `index.html`:
@ -536,14 +518,14 @@ modified to serve `index.html`:
</code-example> </code-example>
- [GitHub Pages](https://pages.github.com/): you can't - [GitHub Pages](https://pages.github.com/): you can't
[directly configure](https://github.com/isaacs/github/issues/408) [directly configure](https://github.com/isaacs/github/issues/408)
the GitHub Pages server, but you can add a 404 page. the GitHub Pages server, but you can add a 404 page.
Copy `index.html` into `404.html`. Copy `index.html` into `404.html`.
It will still be served as the 404 response, but the browser will process that page and load the app properly. It will still be served as the 404 response, but the browser will process that page and load the app properly.
It's also a good idea to It's also a good idea to
[serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch) [serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
and to and to
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm) [create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a - [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
@ -571,7 +553,7 @@ to a server other than the application's own host server.
Browsers forbid such requests unless the server permits them explicitly. Browsers forbid such requests unless the server permits them explicitly.
There isn't anything the client application can do about these errors. There isn't anything the client application can do about these errors.
The server must be configured to accept the application's requests. The server must be configured to accept the application's requests.
Read about how to enable CORS for specific servers at Read about how to enable CORS for specific servers at
<a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>. <a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>.

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

@ -10,12 +10,12 @@ Validate user's form entries.
{@a top} {@a top}
Improve overall data quality by validating user input for accuracy and completeness. Improve overall data quality by validating user input for accuracy and completeness.
This cookbook shows how to validate user input in the UI and display useful validation messages This cookbook shows how to validate user input in the UI and display useful validation messages
using first the template-driven forms and then the reactive forms approach. using first the template-driven forms and then the reactive forms approach.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Read more about these choices in the [Forms](guide/forms) Read more about these choices in the [Forms](guide/forms)
and the [Reactive Forms](guide/reactive-forms) guides. and the [Reactive Forms](guide/reactive-forms) guides.
@ -55,24 +55,23 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a template1} {@a template1}
## Simple template-driven forms ## Simple template-driven forms
In the template-driven approach, you arrange In the template-driven approach, you arrange
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template. [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
You add Angular form directives (mostly directives beginning `ng...`) to help You add Angular form directives (mostly directives beginning `ng...`) to help
Angular construct a corresponding internal control model that implements form functionality. Angular construct a corresponding internal control model that implements form functionality.
In template-drive forms, the control model is _implicit_ in the template. In template-drive forms, the control model is _implicit_ in the template.
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
to the elements. Angular interprets those as well, adding validator functions to the control model. to the elements. Angular interprets those as well, adding validator functions to the control model.
Angular exposes information about the state of the controls including Angular exposes information about the state of the controls including
whether the user has "touched" the control or made changes and if the control values are valid. whether the user has "touched" the control or made changes and if the control values are valid.
In this first template validation example, 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>
@ -86,7 +85,7 @@ with an Angular form control called `name` in its internal control model.
- The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property. - The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
- The template variable (`#name`) has the value `"ngModel"` (always `ngModel`). - The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
This gives you a reference to the Angular `NgModel` directive This gives you a reference to the Angular `NgModel` directive
associated with this control that you can use _in the template_ associated with this control that you can use _in the template_
to check for control states such as `valid` and `dirty`. to check for control states such as `valid` and `dirty`.
@ -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>
@ -149,38 +142,33 @@ Here are the complete files for the first version of `HeroFormTemplateCompononen
{@a template2} {@a template2}
## Template-driven forms with validation messages in code ## Template-driven forms with validation messages in code
While the layout is straightforward, While the layout is straightforward,
there are obvious shortcomings with the way it's handling validation messages: there are obvious shortcomings with the way it's handling validation messages:
* It takes a lot of HTML to represent all possible error conditions. * It takes a lot of HTML to represent all possible error conditions.
This gets out of hand when there are many controls and many validation rules. This gets out of hand when there are many controls and many validation rules.
* There's a lot of JavaScript logic in the HTML. * There's a lot of JavaScript logic in the HTML.
* The messages are static strings, hard-coded into the template. * The messages are static strings, hard-coded into the template.
It's easier to maintain _dynamic_ messages in the component class. It's easier to maintain _dynamic_ messages in the component class.
In this example, you can move the logic and the messages into the component with a few changes to In this example, you can move the logic and the messages into the component with a few changes to
the template and component. 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:
@ -188,7 +176,7 @@ The `<input>` element HTML is almost the same. There are noteworthy differences:
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive. - There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)). It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)).
See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information
on custom validation directives. on custom validation directives.
- The `#name` template variable is gone because the app no longer refers to the Angular control for this element. - The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
@ -198,17 +186,16 @@ on custom validation directives.
{@a component-class} {@a component-class}
### Component class ### Component class
The original component code for Template 1 stayed the same; however, The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code Template 2 requires some changes in the component. This section covers the code
necessary in Template 2's component class to acquire the Angular necessary in Template 2's component class to acquire the Angular
form control and compose error messages. form control and compose error messages.
The first step is to acquire the form control that Angular created from the template by querying for it. The first step is to acquire the form control that Angular created from the template by querying for it.
Look back at the top of the component template 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,32 +203,30 @@ 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>
Some observations: Some observations:
- Angular `@ViewChild` queries for a template variable when you pass it - Angular `@ViewChild` queries for a template variable when you pass it
the name of that variable as a string (`'heroForm'` in this case). the name of that variable as a string (`'heroForm'` in this case).
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero. - The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes. Periodically inspecting it reveals these changes.
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks) - Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks)
when anything changes in the view. when anything changes in the view.
That's the right time to see if there's a new `heroForm` object. 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>
The `onValueChanged` handler interprets user data entry. The `onValueChanged` handler interprets user data entry.
The `data` object passed into the handler contains the current element values. The `data` object passed into the handler contains the current element values.
The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object. The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object.
@ -251,14 +236,13 @@ The messages are empty strings when the hero data are valid.
For each field, the `onValueChanged` handler does the following: For each field, the `onValueChanged` handler does the following:
- Clears the prior error message, if any. - Clears the prior error message, if any.
- Acquires the field's corresponding Angular form control. - Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty") - If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors. _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
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>
@ -269,16 +253,16 @@ Now every time the user makes a change, the `onValueChanged` handler checks for
{@a improvement} {@a improvement}
### The benefits of messages in code ### The benefits of messages in code
Clearly the template got substantially smaller while the component code got substantially larger. Clearly the template got substantially smaller while the component code got substantially larger.
It's not easy to see the benefit when there are just three fields and only two of them have validation rules. It's not easy to see the benefit when there are just three fields and only two of them have validation rules.
Consider what happens as the number of validated Consider what happens as the number of validated
fields and rules increases. fields and rules increases.
In general, HTML is harder to read and maintain than code. In general, HTML is harder to read and maintain than code.
The initial template was already large and threatening to get rapidly worse The initial template was already large and threatening to get rapidly worse
with the addition of more validation message `<div>` elements. with the addition of more validation message `<div>` elements.
After moving the validation messaging to the component, After moving the validation messaging to the component,
the template grows more slowly and proportionally. the template grows more slowly and proportionally.
Each field has approximately the same number of lines no matter its number of validation rules. Each field has approximately the same number of lines no matter its number of validation rules.
The component also grows proportionally, at the rate of one line per validated field The component also grows proportionally, at the rate of one line per validated field
@ -286,7 +270,7 @@ and one line per validation message.
Both trends are manageable. Both trends are manageable.
Now that the messages are in code, you have more flexibility and can compose messages more efficiently. Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server. You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code. In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
@ -294,16 +278,15 @@ In short, there are more opportunities to improve message handling now that text
{@a formmodule} {@a formmodule}
### _FormModule_ and template-driven forms ### _FormModule_ and template-driven forms
Angular has two different forms modules&mdash;`FormsModule` and Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the `ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come two approaches to form development. Both modules come
from the same `@angular/forms` library package. from the same `@angular/forms` library package.
You've been reviewing the "Template-driven" approach which requires the `FormsModule`. You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
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>
@ -313,7 +296,7 @@ Here's how you imported it in the `HeroFormTemplateModule`.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
form template in this cookbook. form template in this cookbook.
They're not germane to the validation story. Look at the [live example](guide/form-validation#live-example) if you're interested. They're not germane to the validation story. Look at the [live example](guide/form-validation#live-example) if you're interested.
@ -326,11 +309,11 @@ They're not germane to the validation story. Look at the [live example](guide/fo
{@a reactive} {@a reactive}
## Reactive forms with validation in code ## Reactive forms with validation in code
In the template-driven approach, you markup the template with form elements, validation attributes, In the template-driven approach, you markup the template with form elements, validation attributes,
and `ng...` directives from the Angular `FormsModule`. and `ng...` directives from the Angular `FormsModule`.
At runtime, Angular interprets the template and derives its _form control model_. At runtime, Angular interprets the template and derives its _form control model_.
**Reactive Forms** takes a different approach. **Reactive Forms** takes a different approach.
You create the form control model in code. You write the template with form elements You create the form control model in code. You write the template with form elements
and `form...` directives from the Angular `ReactiveFormsModule`. and `form...` directives from the Angular `ReactiveFormsModule`.
At runtime, Angular binds the template elements to your control model based on your instructions. At runtime, Angular binds the template elements to your control model based on your instructions.
@ -350,12 +333,11 @@ 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>
The reactive forms feature module and component are in the `src/app/reactive` folder. The reactive forms feature module and component are in the `src/app/reactive` folder.
Focus on the `HeroFormReactiveComponent` there, starting with its template. Focus on the `HeroFormReactiveComponent` there, starting with its template.
@ -363,11 +345,10 @@ Focus on the `HeroFormReactiveComponent` there, starting with its template.
### Component template ### Component template
Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
to the `heroForm` property in the component class. 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,35 +356,30 @@ 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:
- The validation attributes are gone (except `required`) because - The validation attributes are gone (except `required`) because
validating happens in code. validating happens in code.
- `required` remains, not for validation purposes (that's in the code), - `required` remains, not for validation purposes (that's in the code),
but rather for css styling and accessibility. but rather for css styling and accessibility.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
(and perhaps the `aria-required` attribute) when the control has the `required` validator function. (and perhaps the `aria-required` attribute) when the control has the `required` validator function.
Until then, apply the `required` attribute _and_ add the `Validator.required` function Until then, apply the `required` attribute _and_ add the `Validator.required` function
to the control model, as you'll see below. to the control model, as you'll see below.
@ -414,7 +390,7 @@ to the control model, as you'll see below.
- The `formControlName` replaces the `name` attribute; it serves the same - The `formControlName` replaces the `name` attribute; it serves the same
purpose of correlating the input with the Angular form control. purpose of correlating the input with the Angular form control.
- The two-way `[(ngModel)]` binding is gone. - The two-way `[(ngModel)]` binding is gone.
The reactive approach does not use data binding to move data into and out of the form controls. The reactive approach does not use data binding to move data into and out of the form controls.
That's all in code. That's all in code.
@ -430,29 +406,24 @@ The retreat from data binding is a principle of the reactive paradigm rather tha
{@a reactive-component-class} {@a reactive-component-class}
### Component class ### Component class
The component class is now responsible for defining and managing the form control model. The component class is now responsible for defining and managing the form control model.
Angular no longer derives the control model from the template so you can no longer query for it. Angular no longer derives the control model from the template so you can no longer query for it.
You can create the Angular form control model explicitly with You can create the Angular form control model explicitly with
the help of the `FormBuilder` class. 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.
@ -467,29 +438,29 @@ A real app would retrieve the hero asynchronously from a data service, a task be
~~~ ~~~
- The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model. - The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
Then it attaches the same `onValueChanged` handler (there's a one line difference) Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanges` event and calls it immediately to the form's `valueChanges` event and calls it immediately
to set error messages for the new control model. to set error messages for the new control model.
{@a formbuilder} {@a formbuilder}
#### _FormBuilder_ declaration #### _FormBuilder_ declaration
The `FormBuilder` declaration object specifies the three controls of the sample's hero form. The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
Each control spec is a control name with an array value. Each control spec is a control name with an array value.
The first array element is the current value of the corresponding hero field. The first array element is the current value of the corresponding hero field.
The optional second value is a validator function or an array of validator functions. The optional second value is a validator function or an array of validator functions.
Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class. Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class.
Angular has stock validators that correspond to the standard HTML validation attributes. Angular has stock validators that correspond to the standard HTML validation attributes.
The `forbiddenNames` validator on the `"name"` control is a custom validator, The `forbiddenNames` validator on the `"name"` control is a custom validator,
discussed in a separate [section below](guide/form-validation#custom-validation). 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.
~~~ ~~~
@ -500,7 +471,7 @@ discussed in a separate [section below](guide/form-validation#custom-validation)
#### Committing hero value changes #### Committing hero value changes
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
Reactive forms do not use data binding to update data model properties. Reactive forms do not use data binding to update data model properties.
The developer decides _when and how_ to update the data model from control values. The developer decides _when and how_ to update the data model from control values.
This sample updates the model twice: This sample updates the model twice:
@ -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>
@ -573,13 +535,12 @@ and to compare all of the files in this cookbook sample.
{@a custom-validation} {@a custom-validation}
## Custom validation ## Custom validation
This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the
template-driven and the reactive form controls. It's in the `src/app/shared` folder template-driven and the reactive form controls. It's in the `src/app/shared` folder
and declared in the `SharedModule`. 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>
@ -587,7 +548,7 @@ Here's the `forbiddenNamevalidator()` function:
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
and returns a validator function. and returns a validator function.
In this sample, the forbidden name is "bob"; In this sample, the forbidden name is "bob";
the validator rejects any hero name containing "bob". the validator rejects any hero name containing "bob".
Elsewhere it could reject "alice" or any name that the configuring regular expression matches. Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
@ -600,10 +561,9 @@ and whose value is an arbitrary dictionary of values that you could insert into
{@a custom-validation-directive} {@a custom-validation-directive}
### Custom validation directive ### Custom validation directive
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>
@ -637,16 +594,16 @@ Here is the rest of the directive to help you get an idea of how it all comes to
~~~ {.l-sub-section} ~~~ {.l-sub-section}
If you are familiar with Angular validations, you may have noticed If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting` that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of rather than `useClass`. The registered validator must be _this instance_ of
the `ForbiddenValidatorDirective`&mdash;the instance in the form with the `ForbiddenValidatorDirective`&mdash;the instance in the form with
its `forbiddenName` property bound to “bob". If you were to replace its `forbiddenName` property bound to “bob". If you were to replace
`useExisting` with `useClass`, then youd be registering a new class instance, one that `useExisting` with `useClass`, then youd be registering a new class instance, one that
doesnt have a `forbiddenName`. doesnt have a `forbiddenName`.
To see this in action, run the example and then type “bob” in the name of Hero Form 2. To see this in action, run the example and then type “bob” in the name of Hero Form 2.
Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again. Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
This time, when you type “bob”, there's no "bob" error message. This time, when you type “bob”, there's no "bob" error message.
@ -656,7 +613,7 @@ This time, when you type “bob”, there's no "bob" error message.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
For more information on attaching behavior to elements, For more information on attaching behavior to elements,
see [Attribute Directives](guide/attribute-directives). see [Attribute Directives](guide/attribute-directives).
@ -677,11 +634,11 @@ Such tests have minimal setup, are quick to write, and easy to maintain.
They do not require the `Angular TestBed` or asynchronous testing practices. They do not require the `Angular TestBed` or asynchronous testing practices.
That's not possible with _template-driven_ forms. That's not possible with _template-driven_ forms.
The template-driven approach relies on Angular to produce the control model and The template-driven approach relies on Angular to produce the control model and
to derive validation rules from the HTML validation attributes. to derive validation rules from the HTML validation attributes.
You must use the `Angular TestBed` to create component test instances, You must use the `Angular TestBed` to create component test instances,
write asynchronous tests, and interact with the DOM. write asynchronous tests, and interact with the DOM.
While not difficult, this takes more time, work and While not difficult, this takes more time, work and
skill&mdash;factors that tend to diminish test code skill&mdash;factors that tend to diminish test code
coverage and quality. coverage and quality.

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}
@ -33,10 +52,9 @@ Developers can tap into key moments in that lifecycle by implementing
one or more of the *lifecycle hook* interfaces in the Angular `core` library. one or more of the *lifecycle hook* interfaces in the Angular `core` library.
Each interface has a single hook method whose name is the interface name prefixed with `ng`. Each interface has a single hook method whose name is the interface name prefixed with `ng`.
For example, the `OnInit` interface has a hook method named `ngOnInit()` For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component: 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,63 +105,45 @@ 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.
Called _once_, after the _first_ `ngOnChanges()`. Called _once_, after the _first_ `ngOnChanges()`.
</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.
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`. Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
</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,42 +204,51 @@ 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.
Called _just before_ Angular destroys the directive/component. Called _just before_ Angular destroys the directive/component.
</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>
@ -573,7 +496,7 @@ Experienced developers agree that components should be cheap and safe to constru
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Misko Hevery, Angular team lead, Misko Hevery, Angular team lead,
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/) [explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
you should avoid complex constructor logic. you should avoid complex constructor logic.
@ -585,7 +508,7 @@ created under test or before you decide to display it.
Constructors should do no more than set the initial local variables to simple values. Constructors should do no more than set the initial local variables to simple values.
An `ngOnInit()` is a good place for a component to fetch its initial data. The An `ngOnInit()` is a good place for a component to fetch its initial data. The
[Tour of Heroes Tutorial](tutorial/toh-pt4) and [HTTP Client](guide/server-communication) [Tour of Heroes Tutorial](tutorial/toh-pt4) and [HTTP Client](guide/server-communication)
guides show how. guides show how.
@ -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>
@ -656,7 +576,7 @@ Here's the sample in action as the user makes changes.
<img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img> <img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img>
</figure> </figure>
The log entries appear as the string value of the *power* property changes. The log entries appear as the string value of the *power* property changes.
But the `ngOnChanges` does not catch changes to `hero.name` But the `ngOnChanges` does not catch changes to `hero.name`
That's surprising at first. That's surprising at first.
@ -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>
@ -694,7 +613,7 @@ so you can see how often `DoCheck` is called. The results are illuminating:
</figure> </figure>
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost. While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency&mdash;after _every_ This hook is called with enormous frequency&mdash;after _every_
change detection cycle no matter where the change occurred. change detection cycle no matter where the change occurred.
It's called over twenty times in this example before the user can do anything. It's called over twenty times in this example before the user can do anything.
@ -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>
@ -816,7 +729,7 @@ In this case, the projected content is the `<my-child>` from the parent.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The telltale signs of *content projection* are twofold: The telltale signs of *content projection* are twofold:
- HTML between component element tags. - HTML between component element tags.
- The presence of `<ng-content>` tags in the component's template. - The presence of `<ng-content>` tags in the component's template.
@ -826,7 +739,7 @@ The telltale signs of *content projection* are twofold:
{@a aftercontent-hooks} {@a aftercontent-hooks}
### AfterContent hooks ### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks. *AfterContent* hooks are similar to the *AfterView* hooks.
The key difference is in the child component. The key difference is in the child component.
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags * The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
@ -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
@ -37,7 +39,7 @@ I didn't make changes here as I'm not sure what the correct style is.
* [Providers](guide/ngmodule#providers "Extend the app with additional services") * [Providers](guide/ngmodule#providers "Extend the app with additional services")
* [Imports](guide/ngmodule#imports "Import components, directives, and pipes for use in component templates") * [Imports](guide/ngmodule#imports "Import components, directives, and pipes for use in component templates")
* [Resolve conflicts](guide/ngmodule#resolve-conflicts "When two directives have the same selector") * [Resolve conflicts](guide/ngmodule#resolve-conflicts "When two directives have the same selector")
<!-- CF: See my comment in the "Resolve diretive conflicts" section below proposing renaming or reorganizing that section. --> <!-- CF: See my comment in the "Resolve diretive conflicts" section below proposing renaming or reorganizing that section. -->
* [Feature modules](guide/ngmodule#feature-modules "Partition the app into feature modules") * [Feature modules](guide/ngmodule#feature-modules "Partition the app into feature modules")
* [Lazy loaded modules](guide/ngmodule#lazy-load "Load modules asynchronously") with the router * [Lazy loaded modules](guide/ngmodule#lazy-load "Load modules asynchronously") with the router
* [Shared modules](guide/ngmodule#shared-module "Create modules for commonly used components, directives, and pipes") * [Shared modules](guide/ngmodule#shared-module "Create modules for commonly used components, directives, and pipes")
@ -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>
@ -390,7 +375,7 @@ implemented with Angular forms in the [template-driven form](guide/forms) style.
You can write Angular form components in You can write Angular form components in
template-driven or template-driven or
[reactive](cookbook/dynamic-form) style. [reactive](cookbook/dynamic-form) style.
<!-- CF: this link goes to a page titled "Dynamic Forms". Should the link text be "dynamic" instead of "reactive"? --> <!-- CF: this link goes to a page titled "Dynamic Forms". Should the link text be "dynamic" instead of "reactive"? -->
The following sample imports the `FormsModule` from `@angular/forms` because The following sample imports the `FormsModule` from `@angular/forms` because
the `ContactComponent` is written in _template-driven_ style. the `ContactComponent` is written in _template-driven_ style.
@ -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>
@ -483,7 +453,7 @@ once you declare the new component, pipe, and directive.
~~~ {.alert.is-critical} ~~~ {.alert.is-critical}
*Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to *Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to
the `AppModule` metadata's declarations. the `AppModule` metadata's declarations.
These directives belong to the `FormsModule`. These directives belong to the `FormsModule`.
@ -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,13 +487,12 @@ 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>
This solves the immediate issue of referencing both directive _types_ in the same file but This solves the immediate issue of referencing both directive _types_ in the same file but
leaves another issue unresolved. leaves another issue unresolved.
You'll learn more about that issue later in this page, in [Resolve directive conflicts](guide/ngmodule#resolve-conflicts). You'll learn more about that issue later in this page, in [Resolve directive conflicts](guide/ngmodule#resolve-conflicts).
@ -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:
@ -663,7 +608,7 @@ Try the example:
## Resolve directive conflicts ## Resolve directive conflicts
<!-- CF: This section describes directive conflicts in detail, but doesn't describe how to resolve them. <!-- CF: This section describes directive conflicts in detail, but doesn't describe how to resolve them.
This section seems like more of an introduction to the next section, "Feature modules". This section seems like more of an introduction to the next section, "Feature modules".
Consider moving this section to be a child section of "Feature modules", or striking "Resolve" from this title. --> Consider moving this section to be a child section of "Feature modules", or striking "Resolve" from this title. -->
An issue arose [earlier](guide/ngmodule#import-name-conflict) when you declared the contact's `HighlightDirective` because An issue arose [earlier](guide/ngmodule#import-name-conflict) when you declared the contact's `HighlightDirective` because
@ -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.
@ -755,7 +695,7 @@ or collection of related utilities.
While you can do everything within the root module, While you can do everything within the root module,
feature modules help you partition the app into areas of specific interest and purpose. feature modules help you partition the app into areas of specific interest and purpose.
<!-- CF: Is this paragraph just restating the previous paragraph? <!-- CF: Is this paragraph just restating the previous paragraph?
If so, I recommend removing it or merging the two --> If so, I recommend removing it or merging the two -->
A feature module collaborates with the root module and with other modules A feature module collaborates with the root module and with other modules
@ -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>
@ -801,7 +740,7 @@ Before `ContactComponent` can bind with `[(ngModel)]`, its `ContactModule` must
~~~ ~~~
You also replaced `BrowserModule` by `CommonModule`, for reasons explained in the You also replaced `BrowserModule` by `CommonModule`, for reasons explained in the
[Should I import BrowserModule or CommonModule?](cookbook/ngmodule-faq) [Should I import BrowserModule or CommonModule?](cookbook/ngmodule-faq)
section of the [NgModule FAQs](cookbook/ngmodule-faq) page. section of the [NgModule FAQs](cookbook/ngmodule-faq) page.
You _declare_ the contact component, directive, and pipe in the module `declarations`. You _declare_ the contact component, directive, and pipe in the module `declarations`.
@ -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`.
@ -868,7 +802,7 @@ Their specifics aren't important to the story and this page doesn't discuss ever
~~~ {.l-sub-section} ~~~ {.l-sub-section}
Examine and download the complete source for this version from Examine and download the complete source for this version from
the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example> the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example>
~~~ ~~~
@ -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>
@ -906,7 +838,7 @@ The `AppModule` has changed modestly:
Some file names bear a `.3` extension that indicates Some file names bear a `.3` extension that indicates
a difference with prior or future versions. a difference with prior or future versions.
The significant differences will be explained in due course. The significant differences will be explained in due course.
<!-- CF: Can you be more specific here? Are the differences explained later in this page or in another page? --> <!-- CF: Can you be more specific here? Are the differences explained later in this page or in another page? -->
~~~ ~~~
@ -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,57 +984,41 @@ 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
[Child routing component](guide/router) section of the [Child routing component](guide/router) section of the
[Routing & Navigation](guide/router) page. [Routing & Navigation](guide/router) page.
The `HeroComponent` is the feature's top component and routing host. The `HeroComponent` is the feature's top component and routing host.
@ -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>
@ -1203,7 +1107,7 @@ and only one provider of it.
`UserService` is an application-wide singleton. `UserService` is an application-wide singleton.
You don't want each module to have its own separate instance. You don't want each module to have its own separate instance.
Yet there is [a real danger](cookbook/ngmodule-faq) of that happening Yet there is [a real danger](cookbook/ngmodule-faq) of that happening
<!-- CF: This link goes to the top of the NgModule FAQs page. <!-- CF: This link goes to the top of the NgModule FAQs page.
It looks like it is supposed to go to a specific question/section within the page. --> It looks like it is supposed to go to a specific question/section within the page. -->
if the `SharedModule` provides the `UserService`. if the `SharedModule` provides the `UserService`.
@ -1226,7 +1130,7 @@ and `TitleComponent` that only appear in the root `AppComponent`.
You didn't include them in the `SharedModule` for reasons just explained. You didn't include them in the `SharedModule` for reasons just explained.
Instead, gather them in a single `CoreModule` that you import once when the app starts Instead, gather them in a single `CoreModule` that you import once when the app starts
and never import anywhere else. and never import anywhere else.
Perform the following steps: Perform the following steps:
@ -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>
@ -1280,8 +1183,8 @@ Yet they're too big and messy to leave loose in the root folder.
Apps often have many singleton services like this sample's `UserService`. Apps often have many singleton services like this sample's `UserService`.
Each must be registered exactly once, in the app root injector, when the application starts. Each must be registered exactly once, in the app root injector, when the application starts.
While many components inject such services in their constructors&mdash;and While many components inject such services in their constructors&mdash;and
therefore require JavaScript `import` statements to import their symbols&mdash;no therefore require JavaScript `import` statements to import their symbols&mdash;no
other component or module should define or re-create the services themselves. other component or module should define or re-create the services themselves.
Their _providers_ aren't shared. Their _providers_ aren't shared.
@ -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>
@ -1429,13 +1319,12 @@ Remember to _import_ the result; don't add it to any other `@NgModule` list.
Only the root `AppModule` should import the `CoreModule`. Only the root `AppModule` should import the `CoreModule`.
[Bad things happen](cookbook/ngmodule-faq) if a lazy-loaded module imports it. [Bad things happen](cookbook/ngmodule-faq) if a lazy-loaded module imports it.
<!-- CF: Again, this link goes to the top of the NgModule FAQs page. <!-- CF: Again, this link goes to the top of the NgModule FAQs page.
It looks like it is supposed to go to a specific question/section within the page. --> It looks like it is supposed to go to a specific question/section within the page. -->
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,14 +381,13 @@ 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>
The `catch()` operator passes the error object from `http` to the `handleError()` method. The `catch()` operator passes the error object from `http` to the `handleError()` method.
The `handleError` method transforms the error into a developer-friendly message, The `handleError` method transforms the error into a developer-friendly message,
logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`. logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`.
@ -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,18 +621,17 @@ 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>
The constructor expects Angular to inject its `Jsonp` service, which The constructor expects Angular to inject its `Jsonp` service, which
is available because `JsonpModule` is in the root `@NgModule` `imports` array is available because `JsonpModule` is in the root `@NgModule` `imports` array
in `app.module.ts`. in `app.module.ts`.
<a id="query-parameters"></a>### Search parameters <a id="query-parameters"></a>### Search parameters
@ -510,33 +651,29 @@ 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>
`Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request `Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request
to the server. to the server.
<a id="wikicomponent"></a>### The WikiComponent <a id="wikicomponent"></a>### The WikiComponent
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,15 +682,15 @@ 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)
in the [Pipes](guide/pipes) page. in the [Pipes](guide/pipes) page.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The [async pipe](guide/pipes) is a good choice in read-only components The [async pipe](guide/pipes) is a good choice in read-only components
where the component has no need to interact with the data. where the component has no need to interact with the data.
`HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list. `HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list.
@ -592,7 +729,7 @@ The application issues two search requests, one for *angular* and one for *http*
Which response arrives first? It's unpredictable. Which response arrives first? It's unpredictable.
When there are multiple requests in-flight, the app should present the responses When there are multiple requests in-flight, the app should present the responses
in the original request order. in the original request order.
In this example, the app must always display the results for the *http* search In this example, the app must always display the results for the *http* search
no matter which response arrives first. no matter which response arrives first.
@ -606,25 +743,20 @@ 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,
there's a lot more RxJS in the "smart" version, there's a lot more RxJS in the "smart" version,
starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators, starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators,
imported as [described above](guide/server-communication#rxjs-library). imported as [described above](guide/server-communication#rxjs-library).
@ -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>
@ -656,10 +786,9 @@ The `search()` method adds each new search box value to that stream via the subj
{@a listen-for-search-terms} {@a listen-for-search-terms}
### Listen for search terms ### Listen for search terms
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>
@ -694,8 +823,8 @@ The server and client application must work together to thwart this attack.
Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests. Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests.
The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly
generated authentication token in a cookie named `XSRF-TOKEN`. generated authentication token in a cookie named `XSRF-TOKEN`.
The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests. The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests.
The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match. The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match.
See the [XSRF topic on the Security page](guide/security) for more information about XSRF and Angular's `XSRFStrategy` counter measures. See the [XSRF topic on the Security page](guide/security) for more information about XSRF and Angular's `XSRFStrategy` counter measures.
@ -706,7 +835,7 @@ See the [XSRF topic on the Security page](guide/security) for more information a
## Override default request headers (and other request options) ## Override default request headers (and other request options)
Request options (such as headers) are merged into the Request options (such as headers) are merged into the
[default _RequestOptions_](https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html "API: BaseRequestOptions") [default _RequestOptions_](https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html "API: BaseRequestOptions")
before the request is processed. before the request is processed.
The `HttpModule` provides these default options via the `RequestOptions` token. The `HttpModule` provides these default options via the `RequestOptions` token.
@ -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,14 +868,13 @@ 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>
You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab. You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab.
If you're short-circuiting the server call with something like the [_in-memory web api_](guide/server-communication#in-mem-web-api), If you're short-circuiting the server call with something like the [_in-memory web api_](guide/server-communication#in-mem-web-api),
try commenting-out the `create` header option, try commenting-out the `create` header option,
set a breakpoint on the POST call, and step through the request processing set a breakpoint on the POST call, and step through the request processing
to verify the header is there. to verify the header is there.
@ -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,21 +899,20 @@ 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>
The *get heroes* scenario would work, The *get heroes* scenario would work,
but since the app can't save changes to a JSON file, it needs a web API server. but since the app can't save changes to a JSON file, it needs a web API server.
Because there isn't a real server for this demo, Because there isn't a real server for this demo,
it substitutes the Angular _in-memory web api_ simulator for the actual XHR backend service. it substitutes the Angular _in-memory web api_ simulator for the actual XHR backend service.
~~~ {.l-sub-section} ~~~ {.l-sub-section}
The in-memory web api is not part of Angular _proper_. The in-memory web api is not part of Angular _proper_.
It's an optional service in its own It's an optional service in its own
<a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API"><i>angular-in-memory-web-api</i></a> <a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API"><i>angular-in-memory-web-api</i></a>
library installed with npm (see `package.json`). library installed with npm (see `package.json`).
@ -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)">
@ -853,7 +975,7 @@ Here is the final, revised version of <span ngio-ex>src/app/app.module.ts</span>
~~~ {.alert.is-important} ~~~ {.alert.is-important}
Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that
the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others. the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others.
~~~ ~~~

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

@ -9,8 +9,8 @@ Anything you can do with Angular in _TypeScript_, you can also do
in JavaScript. Translating from one language to the other is mostly a in JavaScript. Translating from one language to the other is mostly a
matter of changing the way you organize your code and access Angular APIs. matter of changing the way you organize your code and access Angular APIs.
_TypeScript_ is a popular language option for Angular development. _TypeScript_ is a popular language option for Angular development.
Most code examples on the Internet as well as on this site are written in _TypeScript_. Most code examples on the Internet as well as on this site are written in _TypeScript_.
This cookbook contains recipes for translating _TypeScript_ This cookbook contains recipes for translating _TypeScript_
code examples to _ES6_ and to _ES5_ so that JavaScript developers code examples to _ES6_ and to _ES5_ so that JavaScript developers
can read and write Angular apps in their preferred dialect. can read and write Angular apps in their preferred dialect.
@ -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.**
@ -38,7 +38,7 @@ code shown in this cookbook.**
## _TypeScript_ to _ES6_ to _ES5_ ## _TypeScript_ to _ES6_ to _ES5_
_TypeScript_ _TypeScript_
<a href="https://www.typescriptlang.org" target="_blank" title='"TypeScript is a typed, superset of JavaScript"'>is a typed superset of _ES6 JavaScript_</a>. <a href="https://www.typescriptlang.org" target="_blank" title='"TypeScript is a typed, superset of JavaScript"'>is a typed superset of _ES6 JavaScript_</a>.
_ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers. _ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers.
The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features. The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features.
@ -48,29 +48,29 @@ The downgrade progression is
* _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_") * _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_")
* _ES6-without-decorators_ to _ES5_ * _ES6-without-decorators_ to _ES5_
When translating from _TypeScript_ to _ES6-with-decorators_, remove When translating from _TypeScript_ to _ES6-with-decorators_, remove
[class property access modifiers](http://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers) [class property access modifiers](http://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers)
such as `public` and `private`. such as `public` and `private`.
Remove most of the Remove most of the
[type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html), [type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html),
such as `:string` and `:boolean` such as `:string` and `:boolean`
but **keep the constructor parameter types which are used for dependency injection**. but **keep the constructor parameter types which are used for dependency injection**.
From _ES6-with-decorators_ to _plain ES6_, remove all
From _ES6-with-decorators_ to _plain ES6_, remove all
[decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
and the remaining types. and the remaining types.
You must declare properties in the class constructor (`this.title = '...'`) rather than in the body of the class. You must declare properties in the class constructor (`this.title = '...'`) rather than in the body of the class.
Finally, from _plain ES6_ to _ES5_, the main missing features are `import` Finally, from _plain ES6_ to _ES5_, the main missing features are `import`
statements and `class` declarations. statements and `class` declarations.
For _plain ES6_ transpilation you can _start_ with a setup similar to the For _plain ES6_ transpilation you can _start_ with a setup similar to the
[_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly. [_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly.
Transpile with [Babel](https://babeljs.io/) using the `es2015` preset. Transpile with [Babel](https://babeljs.io/) using the `es2015` preset.
To use decorators and annotations with Babel, install the To use decorators and annotations with Babel, install the
[`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well. [`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well.
{@a modularity} {@a modularity}
@ -82,37 +82,28 @@ To use decorators and annotations with Babel, install the
In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements. In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements.
In _ES5_, you access the Angular entities of the [the Angular packages](glossary) In _ES5_, you access the Angular entities of the [the Angular packages](glossary)
through the global `ng` object. 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
@ -120,56 +111,47 @@ Anything you can import from `@angular` is a nested member of this `ng` object:
Each file in a _TypeScript_ or _ES6_ Angular application constitutes an _ES6_ module. Each file in a _TypeScript_ or _ES6_ Angular application constitutes an _ES6_ module.
When you want to make something available to other modules, you `export` it. When you want to make something available to other modules, you `export` it.
_ES5_ lacks native support for modules. _ES5_ lacks native support for modules.
In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`. In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`.
~~~ {.alert.is-important} ~~~ {.alert.is-important}
The order of `<script>` tags is often significant. The order of `<script>` tags is often significant.
You must load a file that defines a public JavaScript entity before a file that references that entity. You must load a file that defines a public JavaScript entity before a file that references that entity.
~~~ ~~~
The best practice in _ES5_ is to create a form of modularity that avoids polluting the global scope. The best practice in _ES5_ is to create a form of modularity that avoids polluting the global scope.
Add one application namespace object such as `app` to the global `document`. Add one application namespace object such as `app` to the global `document`.
Then each code file "exports" public entities by attaching them to that namespace object, e.g., `app.HeroComponent`. Then each code file "exports" public entities by attaching them to that namespace object, e.g., `app.HeroComponent`.
You could factor a large application into several sub-namespaces You could factor a large application into several sub-namespaces
which leads to "exports" along the lines of `app.heroQueries.HeroComponent`. which leads to "exports" along the lines of `app.heroQueries.HeroComponent`.
Every _ES5_ file should wrap code in an Every _ES5_ file should wrap code in an
[Immediately Invoked Function Expression (IIFE)](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression) [Immediately Invoked Function Expression (IIFE)](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)
to limit unintentional leaking of private symbols into the global scope. 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>
@ -232,7 +205,7 @@ Then use `module.exports` and `require` to export and import application code.
Most Angular _TypeScript_ and _ES6_ code is written as classes. Most Angular _TypeScript_ and _ES6_ code is written as classes.
Properties and method parameters of _TypeScript_ classes may be marked with the access modifiers Properties and method parameters of _TypeScript_ classes may be marked with the access modifiers
`private`, `internal`, and `public`. `private`, `internal`, and `public`.
Remove these modifiers when translating to JavaScript. Remove these modifiers when translating to JavaScript.
Most type declarations (e.g, `:string` and `:boolean`) should be removed when translating to JavaScript. Most type declarations (e.g, `:string` and `:boolean`) should be removed when translating to JavaScript.
@ -242,47 +215,38 @@ Look for types in _TypeScript_ property declarations.
In general it is better to initialize such properties with default values because In general it is better to initialize such properties with default values because
many browser JavaScript engines can generate more performant code. many browser JavaScript engines can generate more performant code.
When _TypeScript_ code follows this same advice, it can infer the property types When _TypeScript_ code follows this same advice, it can infer the property types
and there is nothing to remove during translation. and there is nothing to remove during translation.
In _ES6-without-decorators_, properties of classes must be assigned inside the constructor. In _ES6-without-decorators_, properties of classes must be assigned inside the constructor.
_ES5_ JavaScript has no classes. _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
When writing in _TypeScript_ or _ES6-with-decorators_, When writing in _TypeScript_ or _ES6-with-decorators_,
provide configuration and metadata by adorning a class with one or more *decorators*. provide configuration and metadata by adorning a class with one or more *decorators*.
For example, you supply metadata to a component class by preceding its definition with a For example, you supply metadata to a component class by preceding its definition with a
[`@Component`](api/core/index/Component-decorator) decorator function whose [`@Component`](api/core/index/Component-decorator) decorator function whose
argument is an object literal with metadata properties. argument is an object literal with metadata properties.
@ -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_.
@ -371,8 +316,8 @@ Note that both the _TypeScript_ and _ES6_ `templateUrl` properties identify the
## _ES5_ DSL ## _ES5_ DSL
This _ES5_ pattern of creating a constructor and annotating it with metadata is so common that Angular This _ES5_ pattern of creating a constructor and annotating it with metadata is so common that Angular
provides a convenience API to make it a little more compact and locates the metadata above the constructor, provides a convenience API to make it a little more compact and locates the metadata above the constructor,
as you would if you wrote in _TypeScript_ or _ES6-with-decorators_. as you would if you wrote in _TypeScript_ or _ES6-with-decorators_.
This _API_ (_Application Programming Interface_) is commonly known as the _ES5 DSL_ (_Domain Specific Language_). This _API_ (_Application Programming Interface_) is commonly known as the _ES5 DSL_ (_Domain Specific Language_).
@ -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>
@ -413,8 +353,8 @@ next to the original _ES5_ version for comparison:
</header> </header>
A **named** constructor displays clearly in the console log A **named** constructor displays clearly in the console log
if the component throws a runtime error. if the component throws a runtime error.
An **unnamed** constructor displays as an anonymous function (e.g., `class0`) An **unnamed** constructor displays as an anonymous function (e.g., `class0`)
which is impossible to find in the source code. which is impossible to find in the source code.
@ -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,14 +377,13 @@ 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>
### DSL for other classes ### DSL for other classes
There are similar DSLs for other decorated classes. There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`: You can define a directive with `ng.core.Directive`:
<code-example> <code-example>
@ -474,7 +412,7 @@ and a pipe with `ng.core.Pipe`:
## Interfaces ## Interfaces
A _TypeScript_ interface helps ensure that a class implements the interface's members correctly. A _TypeScript_ interface helps ensure that a class implements the interface's members correctly.
We strongly recommend Angular interfaces where appropriate. We strongly recommend Angular interfaces where appropriate.
For example, the component class that implements the `ngOnInit` lifecycle hook method For example, the component class that implements the `ngOnInit` lifecycle hook method
should implement the `OnInit` interface. should implement the `OnInit` interface.
@ -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>
@ -527,67 +454,56 @@ Just implement the methods and ignore interfaces when translating code samples f
### Input and Output Decorators ### Input and Output Decorators
In _TypeScript_ and _ES6-with-decorators_, you often add metadata to class _properties_ with _property decorators_. In _TypeScript_ and _ES6-with-decorators_, you often add metadata to class _properties_ with _property decorators_.
For example, you apply [`@Input` and `@Output` property decorators](guide/template-syntax) For example, you apply [`@Input` and `@Output` property decorators](guide/template-syntax)
to public class properties that will be the target of data binding expressions in parent components. to public class properties that will be the target of data binding expressions in parent components.
There is no equivalent of a property decorator in _ES5_ or _plain ES6_. There is no equivalent of a property decorator in _ES5_ or _plain ES6_.
Fortunately, every property decorator has an equivalent representation in a class decorator metadata property. Fortunately, every property decorator has an equivalent representation in a class decorator metadata property.
A _TypeScript_ `@Input` property decorator can be represented by an item in the `Component` metadata's `inputs` array. A _TypeScript_ `@Input` property decorator can be represented by an item in the `Component` metadata's `inputs` array.
You already know how to add `Component` or `Directive` class metadata in _any_ JavaScript dialect so You already know how to add `Component` or `Directive` class metadata in _any_ JavaScript dialect so
there's nothing fundamentally new about adding another property. there's nothing fundamentally new about adding another property.
But note that what would have been _separate_ `@Input` and `@Output` property decorators for each class property are But note that what would have been _separate_ `@Input` and `@Output` property decorators for each class property are
combined in the metadata `inputs` and `outputs` _arrays_. 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`)
differs from the corresponding class property name (`notOkMsg`). differs from the corresponding class property name (`notOkMsg`).
That's OK but you must tell Angular about it so that it can map an external binding of `cancelMsg` That's OK but you must tell Angular about it so that it can map an external binding of `cancelMsg`
to the component's `notOkMsg` property. to the component's `notOkMsg` property.
In _TypeScript_ and _ES6-with-decorators_, In _TypeScript_ and _ES6-with-decorators_,
you specify the special binding name in the argument to the property decorator. you specify the special binding name in the argument to the property decorator.
In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata. In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata.
## Dependency Injection ## Dependency Injection
Angular relies heavily on [Dependency Injection](guide/dependency-injection) to provide services to the objects it creates. Angular relies heavily on [Dependency Injection](guide/dependency-injection) to provide services to the objects it creates.
When Angular creates a new component, directive, pipe or another service, When Angular creates a new component, directive, pipe or another service,
it sets the class constructor parameters to instances of services provided by an _Injector_. it sets the class constructor parameters to instances of services provided by an _Injector_.
The developer must tell Angular what to inject into each parameter. The developer must tell Angular what to inject into each parameter.
@ -595,16 +511,16 @@ The developer must tell Angular what to inject into each parameter.
### Injection by Class Type ### Injection by Class Type
The easiest and most popular technique in _TypeScript_ and _ES6-with-decorators_ is to set the constructor parameter type The easiest and most popular technique in _TypeScript_ and _ES6-with-decorators_ is to set the constructor parameter type
to the class associated with the service to inject. to the class associated with the service to inject.
The _TypeScript_ transpiler writes parameter type information into the generated JavaScript. The _TypeScript_ transpiler writes parameter type information into the generated JavaScript.
Angular reads that information at runtime and locates the corresponding service in the appropriate _Injector_.. Angular reads that information at runtime and locates the corresponding service in the appropriate _Injector_..
The _ES6-with-decorators_ transpiler does essentially the same thing using the same parameter-typing syntax. The _ES6-with-decorators_ transpiler does essentially the same thing using the same parameter-typing syntax.
_ES5_ and _plain ES6_ lack types so you must identify "injectables" by attaching a **`parameters`** array to the constructor function. _ES5_ and _plain ES6_ lack types so you must identify "injectables" by attaching a **`parameters`** array to the constructor function.
Each item in the array specifies the service's injection token. Each item in the array specifies the service's injection token.
As with _TypeScript_ the most popular token is a class, As with _TypeScript_ the most popular token is a class,
or rather a _constructor function_ that represents a class in _ES5_ and _plain ES6_. or rather a _constructor function_ that represents a class in _ES5_ and _plain ES6_.
The format of the `parameters` array varies: The format of the `parameters` array varies:
@ -614,43 +530,32 @@ The format of the `parameters` array varies:
When writing with _ES5 DSL_, set the `Class.constructor` property to When writing with _ES5 DSL_, set the `Class.constructor` property to
an array whose first parameters are the injectable constructor functions and whose an array whose first parameters are the injectable constructor functions and whose
last parameter is the class constructor itself. 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
@ -669,49 +574,38 @@ Each item constains a new instance of `Inject`:
* _ES5_ &mdash; simply list the string tokens. * _ES5_ &mdash; simply list the string tokens.
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter. 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
You can qualify injection behavior with injection decorators from `@angular/core`. You can qualify injection behavior with injection decorators from `@angular/core`.
In _TypeScript_ and _ES6-with-decorators_, In _TypeScript_ and _ES6-with-decorators_,
you precede the constructor parameters with injection qualifiers such as: you precede the constructor parameters with injection qualifiers such as:
* [`@Optional`](api/core/index/Optional-decorator) sets the parameter to `null` if the service is missing * [`@Optional`](api/core/index/Optional-decorator) sets the parameter to `null` if the service is missing
* [`@Attribute`](api/core/index/Attribute-interface) to inject a host element attribute value * [`@Attribute`](api/core/index/Attribute-interface) to inject a host element attribute value
@ -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>
@ -769,7 +652,7 @@ array as before. Use a nested array to define a parameter's complete injection s
In the example above, there is no provider for the `'titlePrefix'` token. In the example above, there is no provider for the `'titlePrefix'` token.
Without `Optional`, Angular would raise an error. Without `Optional`, Angular would raise an error.
With `Optional`, Angular sets the constructor parameter to `null` With `Optional`, Angular sets the constructor parameter to `null`
and the component displays the title without a prefix. and the component displays the title without a prefix.
@ -788,12 +671,12 @@ element whose tag matches the component selector.
In _TypeScript_ and _ES6-with-decorators_, you can use host property decorators to bind a host In _TypeScript_ and _ES6-with-decorators_, you can use host property decorators to bind a host
element to a component or directive. element to a component or directive.
The [`@HostBinding`](api/core/index/HostBinding-interface) decorator The [`@HostBinding`](api/core/index/HostBinding-interface) decorator
binds host element properties to component data properties. binds host element properties to component data properties.
The [`@HostListener`](api/core/index/HostListener-interface) decorator binds The [`@HostListener`](api/core/index/HostListener-interface) decorator binds
host element events to component event handlers. host element events to component event handlers.
In _plain ES6_ or _ES5_, add a `host` attribute to the component metadata to achieve the In _plain ES6_ or _ES5_, add a `host` attribute to the component metadata to achieve the
same effect as `@HostBinding` and `@HostListener`. same effect as `@HostBinding` and `@HostListener`.
The `host` value is an object whose properties are host property and listener bindings: The `host` value is an object whose properties are host property and listener bindings:
@ -802,65 +685,49 @@ 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
Some developers prefer to specify host properties and listeners Some developers prefer to specify host properties and listeners
in the component metadata. in the component metadata.
They'd _rather_ do it the way you _must_ do it _ES5_ and _plain ES6_. They'd _rather_ do it the way you _must_ do it _ES5_ and _plain ES6_.
The following re-implementation of the `HeroComponent` reminds us that _any property metadata decorator_ The following re-implementation of the `HeroComponent` reminds us that _any property metadata decorator_
can be expressed as component or directive metadata in both _TypeScript_ and _ES6-with-decorators_. 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>
@ -877,49 +744,40 @@ Several _property_ decorators query a component's nested view and content compon
_View_ children are associated with element tags that appear _within_ the component's template. _View_ children are associated with element tags that appear _within_ the component's template.
_Content_ children are associated with elements that appear _between_ the component's element tags; _Content_ children are associated with elements that appear _between_ the component's element tags;
they are projected into an `<ng-content>` slot in the component's template. they are projected into an `<ng-content>` slot in the component's template.
~~~ ~~~
The [`@ViewChild`](api/core/index/ViewChild-decorator) and The [`@ViewChild`](api/core/index/ViewChild-decorator) and
[`@ViewChildren`](api/core/index/ViewChildren-decorator) property decorators [`@ViewChildren`](api/core/index/ViewChildren-decorator) property decorators
allow a component to query instances of other components that are used in allow a component to query instances of other components that are used in
its view. its view.
In _ES5_ and _ES6_, you access a component's view children by adding a `queries` property to the component metadata. In _ES5_ and _ES6_, you access a component's view children by adding a `queries` property to the component metadata.
The `queries` property value is a hash map. The `queries` property value is a hash map.
* each _key_ is the name of a component property that will hold the view child or children. * each _key_ is the name of a component property that will hold the view child or children.
* each _value_ is a new instance of either `ViewChild` or `ViewChildren`. * 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,41 +789,32 @@ 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>
~~~ {.alert.is-helpful} ~~~ {.alert.is-helpful}
In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata
instead of the `@ViewChild` and `@ContentChild` property decorators. instead of the `@ViewChild` and `@ContentChild` property decorators.
~~~ ~~~
@ -976,7 +825,7 @@ instead of the `@ViewChild` and `@ContentChild` property decorators.
## AOT Compilation in _TypeScript_ only ## AOT Compilation in _TypeScript_ only
Angular offers two modes of template compilation, JIT (_Just-in-Time_) and Angular offers two modes of template compilation, JIT (_Just-in-Time_) and
[AOT (_Ahead-of-Time_)](guide/aot-compiler). [AOT (_Ahead-of-Time_)](guide/aot-compiler).
Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates
_TypeScript_ files as an intermediate result. _TypeScript_ files as an intermediate result.

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").
npm start
<code-example language="sh" class="code-shell"> :marked
npm start 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.
</code-example>
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser. 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.
@ -550,7 +511,7 @@ Even with a 300ms pause between requests, you could have multiple HTTP requests
and they may not return in the order sent. and they may not return in the order sent.
`switchMap()` preserves the original request order while returning `switchMap()` preserves the original request order while returning
only the observable from the most recent `http` method call. only the observable from the most recent `http` method call.
Results from prior calls are canceled and discarded. Results from prior calls are canceled and discarded.
If the search text is empty, the `http()` method call is also short circuited If the search text is empty, the `http()` method call is also short circuited
@ -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>