docs(aio): remove unnecessary blank lines
This commit is contained in:
parent
0aa90c6be4
commit
46f8a6dd85
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||||
|
@ -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>
|
@ -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 — here and in most documentation samples — is `my-app`
|
The `AppComponent` selector — here and in most documentation samples — 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>
|
||||||
|
@ -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—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 — 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 — 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_.
|
@ -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.
|
@ -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 Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
|
[Typed 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,
|
||||||
|
@ -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 — *any* decorator —
|
|||||||
<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) — a `provide` object literal with a `useExisting` definition —
|
We write an [*alias provider*](guide/cb-dependency-injection#useexisting) — a `provide` object literal with a `useExisting` definition —
|
||||||
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
|
||||||
|
@ -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 — without any changes to the source code.
|
and performs AOT compilation — 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 [_<base href="..."/>_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
|
The HTML [_<base href="..."/>_](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 —
|
or merely refreshing the browser while on the hero detail page —
|
||||||
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>.
|
||||||
|
|
||||||
|
@ -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>)—which is *not* the same character as a single
|
||||||
|
quote (`'`)—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>
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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>
|
||||||
|
@ -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—a set for each validated property with
|
Next, the component needs some error messages of course—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—`FormsModule` and
|
Angular has two different forms modules—`FormsModule` and
|
||||||
`ReactiveFormsModule`—that correspond with the
|
`ReactiveFormsModule`—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`—the instance in the form with
|
the `ForbiddenValidatorDirective`—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 you’d be registering a new class instance, one that
|
`useExisting` with `useClass`, then you’d be registering a new class instance, one that
|
||||||
doesn’t have a `forbiddenName`.
|
doesn’t 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—factors that tend to diminish test code
|
skill—factors that tend to diminish test code
|
||||||
coverage and quality.
|
coverage and quality.
|
@ -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>
|
||||||
|
|
||||||
Here’s the code for the final version of the application:
|
Here’s 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>
|
||||||
|
|
||||||
|
@ -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"—also referred to as a "key"—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.
|
||||||
|
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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.
|
||||||
|
@ -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—after _every_
|
This hook is called with enormous frequency—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>
|
||||||
|
@ -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—*component*, *directive*, and *pipe* classes—that
|
A list of declarations—*component*, *directive*, and *pipe* classes—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>
|
||||||
|
|
||||||
|
@ -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`—or the `FORMS_DIRECTIVES`—to
|
*Do not* add `NgModel`—or the `FORMS_DIRECTIVES`—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—and
|
While many components inject such services in their constructors—and
|
||||||
therefore require JavaScript `import` statements to import their symbols—no
|
therefore require JavaScript `import` statements to import their symbols—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>
|
||||||
|
@ -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—which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**—
|
|||||||
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—much faster than a deep check for
|
An object reference check is fast—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>
|
||||||
|
@ -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}
|
||||||
|
@ -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` that’s inside a `FormGroup`, use dot notation to path to the control.
|
To get the state of a `FormControl` that’s 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).
|
|||||||
You’ll need to redefine the form model in the `HeroDetailComponent` constructor,
|
You’ll 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
@ -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>
|
||||||
|
@ -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.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
|
@ -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 *<title>*
|
## The problem with *<title>*
|
||||||
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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).
|
@ -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 ..."`, like this.
|
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`, 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
@ -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_ — simply list the string tokens.
|
* _ES5_ — 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
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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—and only one—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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
@ -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—just a text box and a list of matching search results.
|
The component template is simple—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>
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user