refactor(aio): move content-related images to `content/images/`
|
@ -66,7 +66,7 @@ The examples in this page are available as a <live-example></live-example>.
|
|||
## Quickstart example: Transitioning between two states
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"></img>
|
||||
<img src="content/images/guide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -172,7 +172,7 @@ controls the timing of switching between one set of styles and the next:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"></img>
|
||||
<img src="content/images/guide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -220,7 +220,7 @@ transitions that apply regardless of which state the animation is in. For exampl
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"></img>
|
||||
<img src="content/images/guide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -237,7 +237,7 @@ regardless of what state it was in before it left.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"></img>
|
||||
<img src="content/images/guide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -247,7 +247,7 @@ The wildcard state `*` also matches `void`.
|
|||
## Example: Entering and leaving
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"></img>
|
||||
<img src="content/images/guide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -258,7 +258,7 @@ entering and leaving of elements:
|
|||
* Enter: `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.
|
||||
|
||||
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" title="hero-list-enter-leave.component.ts (excerpt)" linenums="false">
|
||||
|
@ -294,7 +294,7 @@ These two common animations have their own aliases:
|
|||
## Example: Entering and leaving from different states
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"></img>
|
||||
<img src="content/images/guide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -313,7 +313,7 @@ This gives you fine-grained control over each transition:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"></img>
|
||||
<img src="content/images/guide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -346,7 +346,7 @@ If you don't provide a unit when specifying dimension, Angular assumes the defau
|
|||
## Automatic property calculation
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"></img>
|
||||
<img src="content/images/guide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -405,7 +405,7 @@ and the delay (or as the *second* value when there is no delay):
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"></img>
|
||||
<img src="content/images/guide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -427,7 +427,7 @@ slight delay of 10 milliseconds as specified in `'0.2s 10 ease-out'`:
|
|||
## Multi-step animations with keyframes
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"></img>
|
||||
<img src="content/images/guide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -461,7 +461,7 @@ offsets receive offsets `0`, `0.5`, and `1`.
|
|||
## Parallel animation groups
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"></img>
|
||||
<img src="content/images/guide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -501,7 +501,7 @@ those callbacks like this:
|
|||
|
||||
|
||||
|
||||
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
|
||||
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
|
||||
`fromState`, `toState` and `totalTime`.
|
||||
|
||||
Those callbacks will fire whether or not an animation is picked up.
|
||||
Those callbacks will fire whether or not an animation is picked up.
|
||||
|
|
|
@ -297,8 +297,8 @@ The _RxJs_ Observable library is an essential Angular dependency published as an
|
|||
|
||||
Luckily, there is a Rollup plugin that modifies _RxJs_
|
||||
to use the ES `import` and `export` statements that Rollup requires.
|
||||
Rollup then preserves the parts of `RxJS` referenced by the application
|
||||
in the final bundle. Using it is straigthforward. Add the following to
|
||||
Rollup then preserves the parts of `RxJS` referenced by the application
|
||||
in the final bundle. Using it is straigthforward. Add the following to
|
||||
the `plugins` array in `rollup-config.js`:
|
||||
|
||||
<code-example path="aot-compiler/rollup-config.js" region="commonjs" title="rollup-config.js (CommonJs to ES2015 Plugin)" linenums="false">
|
||||
|
@ -307,7 +307,7 @@ the `plugins` array in `rollup-config.js`:
|
|||
*Minification*
|
||||
|
||||
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:
|
||||
|
||||
<code-example path="aot-compiler/rollup-config.js" region="uglify" title="rollup-config.js (CommonJs to ES2015 Plugin)" linenums="false">
|
||||
|
@ -317,7 +317,7 @@ Add the following to the `plugins` array:
|
|||
|
||||
In a production setting, you would also enable gzip on the web server to compress
|
||||
the code into an even smaller package going over the wire.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{@a run-rollup}
|
||||
|
@ -602,8 +602,8 @@ showing exactly which application and Angular modules and classes are included i
|
|||
|
||||
Here's the map for _Tour of Heroes_.
|
||||
|
||||
<a href="assets/images/guide/aot-compiler/toh-pt6-bundle.png" title="View larger image">
|
||||
<a href="content/images/guide/aot-compiler/toh-pt6-bundle.png" title="View larger image">
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/aot-compiler/toh-pt6-bundle.png" alt="toh-pt6-bundle"></img>
|
||||
<img src="content/images/guide/aot-compiler/toh-pt6-bundle.png" alt="toh-pt6-bundle"></img>
|
||||
</figure>
|
||||
</a>
|
||||
|
|
|
@ -27,7 +27,7 @@ You'll learn the details in the pages that follow. For now, focus on the big pic
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"></img>
|
||||
<img src="content/images/guide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -64,23 +64,23 @@ Learn these building blocks, and you're on your way.
|
|||
## Modules
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
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.
|
||||
This page introduces modules; the [Angular modules](guide/ngmodule) page covers them in depth.
|
||||
<br class="l-clear-both"><br>
|
||||
|
||||
Every Angular app has at least one Angular module class, [the _root module_](guide/appmodule "AppModule: the root module"),
|
||||
Every Angular app has at least one Angular module class, [the _root module_](guide/appmodule "AppModule: the root module"),
|
||||
conventionally named `AppModule`.
|
||||
|
||||
While the _root module_ may be the only module in a small application, most apps have many more
|
||||
While the _root module_ may be the only module in a small application, most apps have many more
|
||||
_feature modules_, each a cohesive block of code dedicated to an application domain,
|
||||
a workflow, or a closely related set of capabilities.
|
||||
a workflow, or a closely related set of capabilities.
|
||||
|
||||
An Angular module, whether a _root_ or _feature_, is a class with an `@NgModule` decorator.
|
||||
|
||||
|
@ -98,7 +98,7 @@ Learn more</a> about decorators on the web.
|
|||
|
||||
|
||||
|
||||
`NgModule` is a decorator function that takes a single metadata object whose properties describe the module.
|
||||
`NgModule` is a decorator function that takes a single metadata object whose properties describe the module.
|
||||
The most important properties are:
|
||||
* `declarations` - the _view classes_ that belong to this module.
|
||||
Angular has three kinds of view classes: [components](guide/architecture#components), [directives](guide/architecture#directives), and [pipes](guide/pipes).
|
||||
|
@ -110,7 +110,7 @@ Angular has three kinds of view classes: [components](guide/architecture#compone
|
|||
* `providers` - creators of [services](guide/architecture#services) that this module contributes to
|
||||
the global collection of services; they become accessible in all parts of the app.
|
||||
|
||||
* `bootstrap` - the main application view, called the _root component_,
|
||||
* `bootstrap` - the main application view, called the _root component_,
|
||||
that hosts all other app views. Only the _root module_ should set this `bootstrap` property.
|
||||
|
||||
Here's a simple root module:
|
||||
|
@ -125,15 +125,15 @@ Here's a simple root module:
|
|||
|
||||
|
||||
|
||||
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root module has no reason to _export_ anything because other components don't need to _import_ the root module.
|
||||
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root module has no reason to _export_ anything because other components don't need to _import_ the root module.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
Launch an application by _bootstrapping_ its root module.
|
||||
Launch an application by _bootstrapping_ its root module.
|
||||
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
|
||||
|
||||
|
||||
|
||||
<code-example path="architecture/src/main.ts" title="src/main.ts" linenums="false">
|
||||
|
||||
|
@ -149,7 +149,7 @@ JavaScript also has its own module system for managing collections of JavaScript
|
|||
It's completely different and unrelated to the Angular module system.
|
||||
|
||||
In JavaScript each _file_ is a module and all objects defined in the file belong to that module.
|
||||
The module declares some objects to be public by marking them with the `export` key word.
|
||||
The module declares some objects to be public by marking them with the `export` key word.
|
||||
Other JavaScript modules use *import statements* to access public objects from other modules.
|
||||
|
||||
|
||||
|
@ -182,12 +182,12 @@ These are two different and _complementary_ module systems. Use them both to wri
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
|
||||
</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 `@angular` prefix.
|
||||
|
||||
|
@ -244,7 +244,7 @@ Learn more from the [Angular modules](guide/ngmodule) page.
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -284,7 +284,7 @@ Your app can take action at each moment in this lifecycle through optional [life
|
|||
## Templates
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -314,7 +314,7 @@ The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -330,7 +330,7 @@ Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom
|
|||
## Metadata
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -375,11 +375,11 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
|
|||
|
||||
* `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`
|
||||
so it can get the list of heroes to display.
|
||||
so it can get the list of heroes to display.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -406,7 +406,7 @@ into actions and value updates. Writing such push/pull logic by hand is tedious,
|
|||
read as any experienced jQuery programmer can attest.
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"></img>
|
||||
<img src="content/images/guide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -454,7 +454,7 @@ from the root of the application component tree through all child components.
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -463,7 +463,7 @@ Data binding plays an important role in communication
|
|||
between a template and its component.<br class="l-clear-both">
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -478,7 +478,7 @@ Data binding is also important for communication between parent and child compon
|
|||
## Directives
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -556,7 +556,7 @@ Of course, you can also write your own directives. Components such as
|
|||
## Services
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -624,7 +624,7 @@ application logic into services and make those services available to components
|
|||
## Dependency injection
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -656,7 +656,7 @@ This is *dependency injection*.
|
|||
The process of `HeroService` injection looks a bit like this:
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/architecture/injector-injects.png" alt="Service"></img>
|
||||
<img src="content/images/guide/architecture/injector-injects.png" alt="Service"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ Two examples are [NgFor](guide/template-syntax#ngFor) and [NgIf](guide/template-
|
|||
Learn about them in the [Structural Directives](guide/structural-directives) guide.
|
||||
|
||||
*Attribute directives* are used as attributes of elements.
|
||||
The built-in [NgStyle](guide/template-syntax#ngStyle) directive in the
|
||||
The built-in [NgStyle](guide/template-syntax#ngStyle) directive in the
|
||||
[Template Syntax](guide/template-syntax) guide, for example,
|
||||
can change several element styles at the same time.
|
||||
|
||||
|
@ -108,7 +108,7 @@ they don't conflict with standard HTML attributes.
|
|||
This also reduces the risk of colliding with third-party directive names.
|
||||
|
||||
Make sure you do **not** prefix the `highlight` directive name with **`ng`** because
|
||||
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
|
||||
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
|
||||
For a simple demo, the short prefix, `my`, helps distinguish your custom directive.
|
||||
|
||||
|
||||
|
@ -116,7 +116,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.
|
||||
Exporting `HighlightDirective` makes it accessible to other components.
|
||||
|
||||
|
@ -168,7 +168,7 @@ Now when the app runs, the `myHighlight` directive highlights the paragraph text
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/attribute-directives/first-highlight.png" alt="First Highlight"></img>
|
||||
<img src="content/images/guide/attribute-directives/first-highlight.png" alt="First Highlight"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -179,7 +179,7 @@ Now when the app runs, the `myHighlight` directive highlights the paragraph text
|
|||
|
||||
### Your directive isn't working?
|
||||
|
||||
Did you remember to add the directive to the `declarations` attribute of `@NgModule`?
|
||||
Did you remember to add the directive to the `declarations` attribute of `@NgModule`?
|
||||
It is easy to forget!
|
||||
Open the console in the browser tools and look for an error like this:
|
||||
|
||||
|
@ -236,7 +236,7 @@ each adorned by the `HostListener` decorator.
|
|||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
@ -280,7 +280,7 @@ the mouse hovers over the `p` and disappears as it moves out.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"></img>
|
||||
<img src="content/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -438,7 +438,7 @@ Here are the harness and directive in action.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"></img>
|
||||
<img src="content/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -491,7 +491,7 @@ Here's how the harness should work when you're done coding.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"></img>
|
||||
<img src="content/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -607,4 +607,4 @@ Now apply that reasoning to the following example:
|
|||
|
||||
* 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.
|
||||
Therefore, the directive property must carry the `@Input` decorator.
|
||||
Therefore, the directive property must carry the `@Input` decorator.
|
||||
|
|
|
@ -123,7 +123,7 @@ Your app greets you with a message:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/cli-quickstart/app-works.png' alt="The app works!"></img>
|
||||
<img src='content/images/guide/cli-quickstart/app-works.png' alt="The app works!"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -161,7 +161,7 @@ Open `src/app/app.component.css` and give the component some style.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"></img>
|
||||
<img src='content/images/guide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ The running application displays three heroes:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/parent-to-child.png" alt="Parent-to-child"></img>
|
||||
<img src="content/images/guide/component-communication/parent-to-child.png" alt="Parent-to-child"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -98,7 +98,7 @@ Here's the `NameParentComponent` demonstrating name variations including a name
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/setter.png" alt="Parent-to-child-setter"></img>
|
||||
<img src="content/images/guide/component-communication/setter.png" alt="Parent-to-child-setter"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -156,7 +156,7 @@ Here's the output of a button-pushing sequence:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"></img>
|
||||
<img src="content/images/guide/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -210,7 +210,7 @@ and the method processes it:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/child-to-parent.gif" alt="Child-to-parent"></img>
|
||||
<img src="content/images/guide/component-communication/child-to-parent.gif" alt="Child-to-parent"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -272,7 +272,7 @@ Here we see the parent and child working together.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/countdown-timer-anim.gif" alt="countdown timer"></img>
|
||||
<img src="content/images/guide/component-communication/countdown-timer-anim.gif" alt="countdown timer"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -309,7 +309,7 @@ must read or write child component values or must call child component methods.
|
|||
When the parent component *class* requires that kind of access,
|
||||
***inject*** the child component into the parent as a *ViewChild*.
|
||||
|
||||
The following example illustrates this technique with the same
|
||||
The following example illustrates this technique with the same
|
||||
[Countdown Timer](guide/component-communication#countdown-timer-example) example.
|
||||
Neither its appearance nor its behavior will change.
|
||||
The child [CountdownTimerComponent](guide/component-communication#countdown-timer-example) is the same as well.
|
||||
|
@ -424,7 +424,7 @@ facilitated by the service:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/component-communication/bidirectional-service.gif" alt="bidirectional-service"></img>
|
||||
<img src="content/images/guide/component-communication/bidirectional-service.gif" alt="bidirectional-service"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -441,4 +441,4 @@ and verify that the history meets expectations:
|
|||
|
||||
|
||||
|
||||
[Back to top](guide/component-communication#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
|
|
@ -171,7 +171,7 @@ Once all the dependencies are in place, the `AppComponent` displays the user inf
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/logged-in-user.png" alt="Logged In User"></img>
|
||||
<img src="content/images/guide/dependency-injection/logged-in-user.png" alt="Logged In User"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -337,7 +337,7 @@ Find this example in <live-example name="dependency-injection-in-action">live co
|
|||
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hero-bios.png" alt="Bios"></img>
|
||||
<img src="content/images/guide/dependency-injection/hero-bios.png" alt="Bios"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -403,7 +403,7 @@ 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:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hero-bio-and-content.png" alt="bio and contact"></img>
|
||||
<img src="content/images/guide/dependency-injection/hero-bio-and-content.png" alt="bio and contact"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -440,7 +440,7 @@ Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest o
|
|||
Here's the `HeroBiosAndContactsComponent` in action.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"></img>
|
||||
<img src="content/images/guide/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -450,7 +450,7 @@ until it finds the logger at the `AppComponent` level. The logger logic kicks in
|
|||
with the gratuitous "!!!", indicating that the logger was found.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"></img>
|
||||
<img src="content/images/guide/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -495,7 +495,7 @@ first without a value (yielding the default color) and then with an assigned col
|
|||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/highlight.png" alt="Highlighted bios"></img>
|
||||
<img src="content/images/guide/dependency-injection/highlight.png" alt="Highlighted bios"></img>
|
||||
</figure>
|
||||
|
||||
{@a providers}
|
||||
|
@ -570,10 +570,10 @@ But not every dependency can be satisfied by creating a new instance of a class.
|
|||
You need other ways to deliver dependency values and that means you need other ways to specify a provider.
|
||||
|
||||
The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
|
||||
It's visually simple: a few properties and the logs produced by a logger.
|
||||
It's visually simple: a few properties and the logs produced by a logger.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px"></img>
|
||||
<img src="content/images/guide/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -595,7 +595,7 @@ The code behind it gives you plenty to think about.
|
|||
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/dependency-injection-in-action#tokens).
|
||||
|
||||
The *definition* object has a required property that specifies how to create the singleton instance of the service. In this case, the property.
|
||||
The *definition* object has a required property that specifies how to create the singleton instance of the service. In this case, the property.
|
||||
|
||||
|
||||
|
||||
|
@ -728,7 +728,7 @@ Now put it to use in a simplified version of the `HeroOfTheMonthComponent`.
|
|||
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger` so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"></img>
|
||||
<img src="content/images/guide/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -743,7 +743,7 @@ Behind the scenes, Angular actually sets the `logger` parameter to the full serv
|
|||
The following image, which displays the logging date, confirms the point:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px"></img>
|
||||
<img src="content/images/guide/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -849,7 +849,7 @@ But *no class* in this application inherits from `MinimalLogger`.
|
|||
|
||||
The `LoggerService` and the `DateLoggerService` _could_ have inherited from `MinimalLogger`.
|
||||
They could have _implemented_ it instead in the manner of an interface.
|
||||
But they did neither.
|
||||
But they did neither.
|
||||
The `MinimalLogger` is used exclusively as a dependency injection token.
|
||||
|
||||
When you use a class this way, it's called a ***class-interface***.
|
||||
|
@ -941,7 +941,7 @@ to display a *sorted* list of heroes.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/sorted-heroes.png" alt="Sorted Heroes"></img>
|
||||
<img src="content/images/guide/dependency-injection/sorted-heroes.png" alt="Sorted Heroes"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1003,8 +1003,8 @@ These complications argue for *avoiding component inheritance*.
|
|||
## Find a parent component by injection
|
||||
|
||||
Application components often need to share information.
|
||||
More loosely coupled techniques such as data binding and service sharing
|
||||
are preferable. But sometimes it makes sense for one component
|
||||
More loosely coupled techniques such as data binding and service sharing
|
||||
are preferable. 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.
|
||||
|
||||
|
@ -1013,7 +1013,7 @@ Although an Angular application is a tree of components,
|
|||
there is no public API for inspecting and traversing that tree.
|
||||
|
||||
There is an API for acquiring a child reference.
|
||||
Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`
|
||||
Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`
|
||||
in the [API Reference](api/).
|
||||
|
||||
There is no public API for acquiring a parent reference.
|
||||
|
@ -1050,7 +1050,7 @@ after injecting an `AlexComponent` into her constructor:
|
|||
|
||||
|
||||
|
||||
Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier
|
||||
Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier
|
||||
is there for safety,
|
||||
the <live-example name="dependency-injection-in-action"></live-example>
|
||||
confirms that the `alex` parameter is set.
|
||||
|
@ -1078,8 +1078,8 @@ whose API your `NewsComponent` understands.
|
|||
|
||||
|
||||
Looking for components that implement an interface would be better.
|
||||
That's not possible because TypeScript interfaces disappear
|
||||
from the transpiled JavaScript, which doesn't support interfaces.
|
||||
That's not possible because TypeScript interfaces disappear
|
||||
from the transpiled JavaScript, which doesn't support interfaces.
|
||||
There's no artifact to look for.
|
||||
|
||||
</div>
|
||||
|
@ -1087,7 +1087,7 @@ There's no artifact to look for.
|
|||
|
||||
|
||||
This isn't necessarily good design.
|
||||
This example is examining *whether a component can
|
||||
This example is examining *whether a component can
|
||||
inject its parent via the parent's base class*.
|
||||
|
||||
The sample's `CraigComponent` explores this question. [Looking back](guide/dependency-injection-in-action#alex),
|
||||
|
@ -1126,7 +1126,7 @@ The parent must cooperate by providing an *alias* to itself in the name of a *cl
|
|||
Recall that Angular always adds a component instance to its own injector;
|
||||
that's why you could inject *Alex* into *Cathy* [earlier](guide/dependency-injection-in-action#known-parent).
|
||||
|
||||
Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)—a `provide` object literal with a `useExisting`
|
||||
Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)—a `provide` object literal with a `useExisting`
|
||||
definition—that creates an *alternative* way to inject the same component instance
|
||||
and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`:
|
||||
|
||||
|
@ -1142,7 +1142,7 @@ and add that provider to the `providers` array of the `@Component` metadata for
|
|||
[Parent](guide/dependency-injection-in-action#parent-token) is the provider's *class-interface* token.
|
||||
The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself.
|
||||
|
||||
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
|
||||
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
|
||||
the same way you've done it before:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" title="parent-finder.component.ts (CarolComponent class)" linenums="false">
|
||||
|
@ -1154,7 +1154,7 @@ the same way you've done it before:
|
|||
Here's *Alex* and family in action:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/alex.png" alt="Alex in action"></img>
|
||||
<img src="content/images/guide/dependency-injection/alex.png" alt="Alex in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1215,7 +1215,7 @@ Here's *Alice*, *Barry* and family in action:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/alice.png" alt="Alice in action"></img>
|
||||
<img src="content/images/guide/dependency-injection/alice.png" alt="Alice in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ The final UI looks like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/displaying-data/final.png" alt="Final UI"></img>
|
||||
<img src="content/images/guide/displaying-data/final.png" alt="Final UI"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -126,7 +126,7 @@ inside the `<my-app>` tag.
|
|||
Now run the app. It should display the title and hero name:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero"></img>
|
||||
<img src="content/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -233,7 +233,7 @@ Now the heroes appear in an unordered list.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/displaying-data/hero-names-list.png" alt="After ngfor"></img>
|
||||
<img src="content/images/guide/displaying-data/hero-names-list.png" alt="After ngfor"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ because it doesn't render any additional output.
|
|||
Take a closer look at the methods in `ad-banner.component.ts`.
|
||||
|
||||
`AdBannerComponent` takes an array of `AdItem` objects as input,
|
||||
which ultimately comes from `AdService`. `AdItem` objects specify
|
||||
the type of component to load and any data to bind to the
|
||||
which ultimately comes from `AdService`. `AdItem` objects specify
|
||||
the type of component to load and any data to bind to the
|
||||
component.`AdService` returns the actual ads making up the ad campaign.
|
||||
|
||||
Passing an array of components to `AdBannerComponent` allows for a
|
||||
|
@ -141,17 +141,17 @@ value to select an `adItem` from the array.
|
|||
|
||||
|
||||
|
||||
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
|
||||
to resolve a `ComponentFactory` for each specific component.
|
||||
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
|
||||
to resolve a `ComponentFactory` for each specific component.
|
||||
The `ComponentFactory` then creates an instance of each component.
|
||||
|
||||
Next, you're targeting the `viewContainerRef` that
|
||||
exists on this specific instance of the component. How do you know it's
|
||||
this specific instance? Because it's referring to `adHost` and `adHost` is the
|
||||
Next, you're targeting the `viewContainerRef` that
|
||||
exists on this specific instance of the component. How do you know it's
|
||||
this specific instance? Because it's referring to `adHost` and `adHost` is the
|
||||
directive you set up earlier to tell Angular where to insert dynamic components.
|
||||
|
||||
As you may recall, `AdDirective` injects `ViewContainerRef` into its constructor.
|
||||
This is how the directive accesses the element that you want to use to host the dynamic component.
|
||||
As you may recall, `AdDirective` injects `ViewContainerRef` into its constructor.
|
||||
This is how the directive accesses the element that you want to use to host the dynamic component.
|
||||
|
||||
To add the component to the template, you call `createComponent()` on `ViewContainerRef`.
|
||||
|
||||
|
@ -214,9 +214,9 @@ Here are two sample components and the `AdComponent` interface for reference:
|
|||
The final ad banner looks like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dynamic-component-loader/ads.gif" alt="Ads"></img>
|
||||
<img src="content/images/guide/dynamic-component-loader/ads.gif" alt="Ads"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
See the <live-example name="dynamic-component-loader"></live-example>.
|
||||
See the <live-example name="dynamic-component-loader"></live-example>.
|
||||
|
|
|
@ -7,20 +7,20 @@ Render dynamic forms with FormGroup.
|
|||
@description
|
||||
|
||||
|
||||
Building handcrafted forms can be costly and time-consuming,
|
||||
Building handcrafted forms can be costly and time-consuming,
|
||||
especially if you need a great number of them, they're similar to each other, and they change frequently
|
||||
to meet rapidly changing business and regulatory requirements.
|
||||
|
||||
It may be more economical to create the forms dynamically, based on
|
||||
It may be more economical to create the forms dynamically, based on
|
||||
metadata that describes the business object model.
|
||||
|
||||
This cookbook shows you how to use `formGroup` to dynamically
|
||||
This cookbook shows you how to use `formGroup` to dynamically
|
||||
render a simple form with different control types and validation.
|
||||
It's a primitive start.
|
||||
It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience.
|
||||
All such greatness has humble beginnings.
|
||||
|
||||
The example in this cookbook is a dynamic form to build an
|
||||
The example in this cookbook is a dynamic form to build an
|
||||
online application experience for heroes seeking employment.
|
||||
The agency is constantly tinkering with the application process.
|
||||
You can create the forms on the fly *without changing the application code*.
|
||||
|
@ -44,8 +44,8 @@ Start by creating an `NgModule` called `AppModule`.
|
|||
|
||||
This cookbook uses [reactive forms](guide/reactive-forms).
|
||||
|
||||
Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`,
|
||||
so in order to access any reactive forms directives, you have to import
|
||||
Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`,
|
||||
so in order to access any reactive forms directives, you have to import
|
||||
`ReactiveFormsModule` from the `@angular/forms` library.
|
||||
|
||||
Bootstrap the `AppModule` in `main.ts`.
|
||||
|
@ -81,12 +81,12 @@ The following `QuestionBase` is a fundamental question class.
|
|||
|
||||
|
||||
|
||||
From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion`
|
||||
From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion`
|
||||
that represent textbox and dropdown questions.
|
||||
The idea is that the form will be bound to specific question types and render the
|
||||
The idea is that the form will be bound to specific question types and render the
|
||||
appropriate controls dynamically.
|
||||
|
||||
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
|
||||
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
|
||||
via the `type` property.
|
||||
|
||||
|
||||
|
@ -106,7 +106,7 @@ via the `type` property.
|
|||
|
||||
|
||||
Next is `QuestionControlService`, a simple service for transforming the questions to a `FormGroup`.
|
||||
In a nutshell, the form group consumes the metadata from the question model and
|
||||
In a nutshell, the form group consumes the metadata from the question model and
|
||||
allows you to specify default values and validation rules.
|
||||
|
||||
|
||||
|
@ -117,7 +117,7 @@ allows you to specify default values and validation rules.
|
|||
{@a form-component}
|
||||
|
||||
## Question form components
|
||||
Now that you have defined the complete model you are ready
|
||||
Now that you have defined the complete model you are ready
|
||||
to create components to represent the dynamic form.
|
||||
|
||||
|
||||
|
@ -139,7 +139,7 @@ to create components to represent the dynamic form.
|
|||
|
||||
It presents a list of questions, each bound to a `<df-question>` component element.
|
||||
The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
|
||||
the component responsible for rendering the details of each _individual_
|
||||
the component responsible for rendering the details of each _individual_
|
||||
question based on values in the data-bound question object.
|
||||
|
||||
|
||||
|
@ -164,8 +164,8 @@ The `ngSwitch` determines which type of question to display.
|
|||
In both components you're relying on Angular's **formGroup** to connect the template HTML to the
|
||||
underlying control objects, populated from the question model with display and validation rules.
|
||||
|
||||
`formControlName` and `formGroup` are directives defined in
|
||||
`ReactiveFormsModule`. The templates can access these directives
|
||||
`formControlName` and `formGroup` are directives defined in
|
||||
`ReactiveFormsModule`. The templates can access these directives
|
||||
directly since you imported `ReactiveFormsModule` from `AppModule`.
|
||||
{@a questionnaire-data}
|
||||
|
||||
|
@ -176,9 +176,9 @@ directly since you imported `ReactiveFormsModule` from `AppModule`.
|
|||
The set of questions you've defined for the job application is returned from the `QuestionService`.
|
||||
In a real app you'd retrieve these questions from storage.
|
||||
|
||||
The key point is that you control the hero job application questions
|
||||
The key point is that you control the hero job application questions
|
||||
entirely through the objects returned from `QuestionService`.
|
||||
Questionnaire maintenance is a simple matter of adding, updating,
|
||||
Questionnaire maintenance is a simple matter of adding, updating,
|
||||
and removing objects from the `questions` array.
|
||||
|
||||
|
||||
|
@ -198,7 +198,7 @@ Finally, display an instance of the form in the `AppComponent` shell.
|
|||
{@a dynamic-template}
|
||||
|
||||
## Dynamic Template
|
||||
Although in this example you're modelling a job application for heroes, there are
|
||||
Although in this example you're modelling a job application for heroes, there are
|
||||
no references to any specific hero question
|
||||
outside the objects returned by `QuestionService`.
|
||||
|
||||
|
@ -217,9 +217,9 @@ Saving and retrieving the data is an exercise for another time.
|
|||
The final form looks like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form"></img>
|
||||
<img src="content/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/dynamic-form#top)
|
||||
[Back to top](guide/dynamic-form#top)
|
||||
|
|
|
@ -41,7 +41,7 @@ the form-specific directives and techniques described in this page.
|
|||
|
||||
|
||||
|
||||
You can also use a reactive (or model-driven) approach to build forms.
|
||||
You can also use a reactive (or model-driven) approach to build forms.
|
||||
However, this page focuses on template-driven forms.
|
||||
|
||||
|
||||
|
@ -60,7 +60,7 @@ You'll learn to build a template-driven form that looks like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/hero-form-1.png" width="400px" alt="Clean Form"></img>
|
||||
<img src="content/images/guide/forms/hero-form-1.png" width="400px" alt="Clean Form"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -74,7 +74,7 @@ If you delete the hero name, the form displays a validation error in an attentio
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"></img>
|
||||
<img src="content/images/guide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -148,7 +148,7 @@ You can create a new hero like this:
|
|||
|
||||
## Create a form component
|
||||
|
||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||
to handle data and user interactions programmatically.
|
||||
Begin with the class because it states, in brief, what the hero editor can do.
|
||||
|
||||
|
@ -301,7 +301,7 @@ You added a *Submit* button at the bottom with some classes on it for styling.
|
|||
|
||||
|
||||
|
||||
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
||||
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
||||
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
|
||||
|
||||
|
||||
|
@ -372,7 +372,7 @@ Running the app right now would be disappointing.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"></img>
|
||||
<img src="content/images/guide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -413,8 +413,8 @@ You left yourself a note to throw it away when you're done.
|
|||
|
||||
Focus on the binding syntax: `[(ngModel)]="..."`.
|
||||
|
||||
You need one more addition to display the data. Declare
|
||||
a template variable for the form. Update the `<form>` tag with
|
||||
You need one more addition to display the data. Declare
|
||||
a template variable for the form. Update the `<form>` tag with
|
||||
`#heroForm="ngForm"` as follows:
|
||||
|
||||
|
||||
|
@ -456,7 +456,7 @@ At some point it might look like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"></img>
|
||||
<img src="content/images/guide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -529,7 +529,7 @@ If you run the app now and change every hero model property, the form might disp
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"></img>
|
||||
<img src="content/images/guide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -642,7 +642,7 @@ The actions and effects are as follows:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition"></img>
|
||||
<img src="content/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -651,7 +651,7 @@ You should see the following transitions and class names:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"></img>
|
||||
<img src="content/images/guide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -671,7 +671,7 @@ on the left of the input box:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"></img>
|
||||
<img src="content/images/guide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -705,7 +705,7 @@ When the user deletes the name, the form should look like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/forms/name-required-error.png" width="400px" alt="Name required"></img>
|
||||
<img src="content/images/guide/forms/name-required-error.png" width="400px" alt="Name required"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -833,8 +833,8 @@ to the hero form component's `onSubmit()` method:
|
|||
|
||||
|
||||
|
||||
You'd already defined a template reference variable,
|
||||
`#heroForm`, and initialized it with the value "ngForm".
|
||||
You'd already defined a template reference variable,
|
||||
`#heroForm`, and initialized it with the value "ngForm".
|
||||
Now, use that variable to access the form with the Submit button.
|
||||
|
||||
|
||||
|
@ -849,7 +849,7 @@ using an event binding. Here's the code:
|
|||
|
||||
|
||||
|
||||
If you run the application now, you find that the button is enabled—although
|
||||
If you run the application now, you find that the button is enabled—although
|
||||
it doesn't do anything useful yet.
|
||||
|
||||
Now if you delete the Name, you violate the "required" rule, which
|
||||
|
|
|
@ -55,7 +55,7 @@ open simultaneously.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"></img>
|
||||
<img src="content/images/guide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -149,7 +149,7 @@ Each tax return component has the following characteristics:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"></img>
|
||||
<img src="content/images/guide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -234,7 +234,7 @@ Component (B) is the parent of another component (C) that defines its own, even
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/car-components.png" alt="car components" width="220"></img>
|
||||
<img src="content/images/guide/dependency-injection/car-components.png" alt="car components" width="220"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -247,7 +247,7 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"></img>
|
||||
<img src="content/images/guide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ The app uses the Angular <code>Http</code> client to communicate via **XMLHttpRe
|
|||
It works like this:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/http/http-toh.gif' alt="ToH mini app" width="250"></img>
|
||||
<img src='content/images/guide/http/http-toh.gif' alt="ToH mini app" width="250"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -333,13 +333,13 @@ and `map()` is one of the RxJS *operators*.
|
|||
|
||||
## RxJS library
|
||||
<a href="http://reactivex.io/rxjs" title="RxJS Reactive Extensions">RxJS</a>
|
||||
is a third party library, endorsed by Angular, that implements the
|
||||
is a third party library, endorsed by Angular, that implements the
|
||||
<a href="https://www.youtube.com/watch?v=VLGCCpOWFFw" 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:
|
||||
But you must take a critical extra step to make RxJS Observables usable:
|
||||
_you must import the RxJS operators individually_.
|
||||
|
||||
### Enable RxJS operators
|
||||
|
@ -646,8 +646,8 @@ highlighting just the parts that are different.
|
|||
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
|
||||
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`.
|
||||
|
@ -680,15 +680,15 @@ Both methods take the same functional arguments.
|
|||
|
||||
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
|
||||
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)
|
||||
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).
|
||||
|
||||
|
||||
|
@ -746,7 +746,7 @@ types in a text box:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/http/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250"></img>
|
||||
<img src='content/images/guide/http/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -831,7 +831,7 @@ turn your attention to the component (template and class) that takes user input
|
|||
The template presents an `<input>` element *search box* to gather search terms from the user,
|
||||
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[]>`).
|
||||
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
|
||||
|
@ -865,7 +865,7 @@ It should only make requests when the user *stops typing*.
|
|||
Here's how it will work after refactoring:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/http/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"></img>
|
||||
<img src='content/images/guide/http/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Angular calls lifecycle hook methods on directives and components as it creates,
|
|||
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"></img>
|
||||
<img src="content/images/guide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -262,7 +262,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||
|
||||
{@a interface-optional}
|
||||
|
||||
|
||||
|
||||
|
||||
## Interfaces are optional (technically)
|
||||
|
||||
|
@ -465,7 +465,7 @@ The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
|||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"></img>
|
||||
<img src="content/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -549,7 +549,7 @@ with an entry in the *Hook Log* as seen here:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"></img>
|
||||
<img src='content/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -672,7 +672,7 @@ Here's the sample in action as the user makes changes.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"></img>
|
||||
<img src='content/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -718,7 +718,7 @@ so you can see how often `DoCheck` is called. The results are illuminating:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"></img>
|
||||
<img src='content/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -788,14 +788,14 @@ Angular's unidirectional data flow rule forbids updates to the view *after* it h
|
|||
Both of these hooks fire _after_ the component's view has been composed.
|
||||
|
||||
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).
|
||||
The `LoggerService.tick_then()` postpones the log update
|
||||
The `LoggerService.tick_then()` postpones the log update
|
||||
for one turn of the browser's JavaScript cycle and that's just long enough.
|
||||
|
||||
|
||||
Here's *AfterView* in action:
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"></img>
|
||||
<img src='content/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -859,7 +859,7 @@ It tells Angular where to insert that content.
|
|||
In this case, the projected content is the `<my-child>` from the parent.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"></img>
|
||||
<img src='content/images/guide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -915,4 +915,4 @@ There's no [need to wait](guide/lifecycle-hooks#wait-a-tick).
|
|||
|
||||
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
||||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||
There is a small window between the `AfterContent...` and `AfterView...` hooks to modify the host view.
|
||||
There is a small window between the `AfterContent...` and `AfterView...` hooks to modify the host view.
|
||||
|
|
|
@ -145,7 +145,7 @@ As you click the button, the displayed date alternates between
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"></img>
|
||||
<img src='content/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -237,7 +237,7 @@ Now you need a component to demonstrate the pipe.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/pipes/power-booster.png' alt="Power Booster"></img>
|
||||
<img src='content/images/guide/pipes/power-booster.png' alt="Power Booster"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -285,7 +285,7 @@ your pipe and two-way data binding with `ngModel`.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"></img>
|
||||
<img src='content/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -372,7 +372,7 @@ code with checkbox switches and additional displays to help you experience these
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"></img>
|
||||
<img src='content/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -559,7 +559,7 @@ The component renders as the following:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/pipes/hero-list.png' alt="Hero List"></img>
|
||||
<img src='content/images/guide/pipes/hero-list.png' alt="Hero List"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -674,4 +674,4 @@ Any capabilities that you would have put in a pipe and shared across the app can
|
|||
written in a filtering/sorting service and injected into the component.
|
||||
|
||||
If these performance and minification considerations don't apply to you, you can always create your own such pipes
|
||||
(similar to the [FlyingHeroesPipe](guide/pipes#impure-flying-heroes)) or find them in the community.
|
||||
(similar to the [FlyingHeroesPipe](guide/pipes#impure-flying-heroes)) or find them in the community.
|
||||
|
|
|
@ -53,57 +53,57 @@ But they diverge markedly in philosophy, programming style, and technique.
|
|||
They even have their own modules: the `ReactiveFormsModule` and the `FormsModule`.
|
||||
|
||||
### _Reactive_ forms
|
||||
Angular _reactive_ forms facilitate a _reactive style_ of programming
|
||||
Angular _reactive_ forms facilitate a _reactive style_ of programming
|
||||
that favors explicit management of the data flowing between
|
||||
a non-UI _data model_ (typically retrieved from a server) and a
|
||||
UI-oriented _form model_ that retains the states
|
||||
and values of the HTML controls on screen. Reactive forms offer the ease
|
||||
a non-UI _data model_ (typically retrieved from a server) and a
|
||||
UI-oriented _form model_ that retains the states
|
||||
and values of the HTML controls on screen. Reactive forms offer the ease
|
||||
of using reactive patterns, testing, and validation.
|
||||
|
||||
With _reactive_ forms, you create a tree of Angular form control objects
|
||||
in the component class and bind them to native form control elements in the
|
||||
component template, using techniques described in this guide.
|
||||
in the component class and bind them to native form control elements in the
|
||||
component template, using techniques described in this guide.
|
||||
|
||||
You create and manipulate form control objects directly in the
|
||||
component class. As the component class has immediate access to both the data
|
||||
model and the form control structure, you can push data model values into
|
||||
the form controls and pull user-changed values back out. The component can
|
||||
You create and manipulate form control objects directly in the
|
||||
component class. As the component class has immediate access to both the data
|
||||
model and the form control structure, you can push data model values into
|
||||
the form controls and pull user-changed values back out. The component can
|
||||
observe changes in form control state and react to those changes.
|
||||
|
||||
One advantage of working with form control objects directly is that value and validity updates
|
||||
are [always synchronous and under your control](guide/reactive-forms#async-vs-sync "Async vs sync").
|
||||
One advantage of working with form control objects directly is that value and validity updates
|
||||
are [always synchronous and under your control](guide/reactive-forms#async-vs-sync "Async vs sync").
|
||||
You won't encounter the timing issues that sometimes plague a template-driven form
|
||||
and reactive forms can be easier to unit test.
|
||||
|
||||
In keeping with the reactive paradigm, the component
|
||||
In keeping with the reactive paradigm, the component
|
||||
preserves the immutability of the _data model_,
|
||||
treating it as a pure source of original values.
|
||||
Rather than update the data model directly,
|
||||
the component extracts user changes and forwards them to an external component or service,
|
||||
which does something with them (such as saving them)
|
||||
and returns a new _data model_ to the component that reflects the updated model state.
|
||||
|
||||
Using reactive form directives does not require you to follow all reactive priniciples,
|
||||
Rather than update the data model directly,
|
||||
the component extracts user changes and forwards them to an external component or service,
|
||||
which does something with them (such as saving them)
|
||||
and returns a new _data model_ to the component that reflects the updated model state.
|
||||
|
||||
Using reactive form directives does not require you to follow all reactive priniciples,
|
||||
but it does facilitate the reactive programming approach should you choose to use it.
|
||||
|
||||
### _Template-driven_ forms
|
||||
|
||||
_Template-driven_ forms, introduced in the [Template guide](guide/forms), take a completely different approach.
|
||||
|
||||
You place HTML form controls (such as `<input>` and `<select>`) in the component template and
|
||||
bind them to _data model_ properties in the component, using directives
|
||||
You place HTML form controls (such as `<input>` and `<select>`) in the component template and
|
||||
bind them to _data model_ properties in the component, using directives
|
||||
like `ngModel`.
|
||||
|
||||
You don't create Angular form control objects. Angular directives
|
||||
create them for you, using the information in your data bindings.
|
||||
You don't create Angular form control objects. Angular directives
|
||||
create them for you, using the information in your data bindings.
|
||||
You don't push and pull data values. Angular handles that for you with `ngModel`.
|
||||
Angular updates the mutable _data model_ with user changes as they happen.
|
||||
|
||||
For this reason, the `ngModel` directive is not part of the ReactiveFormsModule.
|
||||
|
||||
While this means less code in the component class,
|
||||
While this means less code in the component class,
|
||||
[template-driven forms are asynchronous](guide/reactive-forms#async-vs-sync "Async vs sync")
|
||||
which may complicate development in more advanced scenarios.
|
||||
which may complicate development in more advanced scenarios.
|
||||
|
||||
|
||||
{@a async-vs-sync}
|
||||
|
@ -113,37 +113,37 @@ which may complicate development in more advanced scenarios.
|
|||
|
||||
Reactive forms are synchronous. Template-driven forms are asynchronous. It's a difference that matters.
|
||||
|
||||
In reactive forms, you create the entire form control tree in code.
|
||||
You can immediately update a value or drill down through the descendents of the parent form
|
||||
In reactive forms, you create the entire form control tree in code.
|
||||
You can immediately update a value or drill down through the descendents of the parent form
|
||||
because all controls are always available.
|
||||
|
||||
Template-driven forms delegate creation of their form controls to directives.
|
||||
To avoid "_changed after checked_" errors,
|
||||
To avoid "_changed after checked_" errors,
|
||||
these directives take more than one cycle to build the entire control tree.
|
||||
That means you must wait a tick before manipulating any of the controls
|
||||
from within the component class.
|
||||
|
||||
For example, if you inject the form control with a `@ViewChild(NgForm)` query and examine it in the
|
||||
For example, if you inject the form control with a `@ViewChild(NgForm)` query and examine it in the
|
||||
[`ngAfterViewInit` lifecycle hook](guide/lifecycle-hooks#afterview "Lifecycle hooks guide: AfterView"),
|
||||
you'll discover that it has no children.
|
||||
You must wait a tick, using `setTimeout`, before you can
|
||||
extract a value from a control, test its validity, or set it to a new value.
|
||||
|
||||
The asynchrony of template-driven forms also complicates unit testing.
|
||||
You must wrap your test block in `async()` or `fakeAsync()` to
|
||||
avoid looking for values in the form that aren't there yet.
|
||||
The asynchrony of template-driven forms also complicates unit testing.
|
||||
You must wrap your test block in `async()` or `fakeAsync()` to
|
||||
avoid looking for values in the form that aren't there yet.
|
||||
With reactive forms, everything is available when you expect it to be.
|
||||
|
||||
### Which is better, reactive or template-driven?
|
||||
### Which is better, reactive or template-driven?
|
||||
|
||||
Neither is "better".
|
||||
They're two different architectural paradigms,
|
||||
They're two different architectural paradigms,
|
||||
with their own strengths and weaknesses.
|
||||
Choose the approach that works best for you.
|
||||
You may decide to use both in the same application.
|
||||
|
||||
The balance of this _reactive forms_ guide explores the _reactive_ paradigm and
|
||||
concentrates exclusively on reactive forms techniques.
|
||||
The balance of this _reactive forms_ guide explores the _reactive_ paradigm and
|
||||
concentrates exclusively on reactive forms techniques.
|
||||
For information on _template-driven forms_, see the [_Forms_](guide/forms) guide.
|
||||
|
||||
In the next section, you'll set up your project for the reactive form demo.
|
||||
|
@ -156,7 +156,7 @@ Then you'll learn about the [Angular form classes](guide/reactive-forms#essentia
|
|||
|
||||
## Setup
|
||||
|
||||
Follow the steps in the [_Setup_ guide](guide/setup "Setup guide")
|
||||
Follow the steps in the [_Setup_ guide](guide/setup "Setup guide")
|
||||
for creating a new project folder (perhaps called `reactive-forms`)
|
||||
based on the _QuickStart seed_.
|
||||
|
||||
|
@ -177,9 +177,9 @@ Create a new `data-model.ts` file in the `app` directory and copy the content be
|
|||
|
||||
|
||||
|
||||
The file exports two classes and two constants. The `Address`
|
||||
and `Hero` classes define the application _data model_.
|
||||
The `heroes` and `states` constants supply the test data.
|
||||
The file exports two classes and two constants. The `Address`
|
||||
and `Hero` classes define the application _data model_.
|
||||
The `heroes` and `states` constants supply the test data.
|
||||
|
||||
|
||||
|
||||
|
@ -187,7 +187,7 @@ The `heroes` and `states` constants supply the test data.
|
|||
|
||||
|
||||
## Create a _reactive forms_ component
|
||||
Make a new file called
|
||||
Make a new file called
|
||||
`hero-detail.component.ts` in the `app` directory and import these symbols:
|
||||
|
||||
|
||||
|
@ -206,8 +206,8 @@ Now enter the `@Component` decorator that specifies the `HeroDetailComponent` me
|
|||
|
||||
|
||||
|
||||
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
|
||||
`FormControl` is a directive that allows you to create and manage
|
||||
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
|
||||
`FormControl` is a directive that allows you to create and manage
|
||||
a `FormControl` instance directly.
|
||||
|
||||
|
||||
|
@ -218,14 +218,14 @@ a `FormControl` instance directly.
|
|||
|
||||
|
||||
|
||||
Here you are creating a `FormControl` called `name`.
|
||||
It will be bound in the template to an HTML `input` box for the hero name.
|
||||
Here you are creating a `FormControl` called `name`.
|
||||
It will be bound in the template to an HTML `input` box for the hero name.
|
||||
|
||||
A `FormControl` constructor accepts three, optional arguments:
|
||||
A `FormControl` constructor accepts three, optional arguments:
|
||||
the initial data value, an array of validators, and an array of async validators.
|
||||
|
||||
This simple control doesn't have data or validators.
|
||||
In real apps, most form controls have both.
|
||||
In real apps, most form controls have both.
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
@ -255,9 +255,9 @@ Now create the component's template, `src/app/hero-detail.component.html`, with
|
|||
|
||||
|
||||
|
||||
To let Angular know that this is the input that you want to
|
||||
associate to the `name` `FormControl` in the class,
|
||||
you need `[formControl]="name"` in the template on the `<input>`.
|
||||
To let Angular know that this is the input that you want to
|
||||
associate to the `name` `FormControl` in the class,
|
||||
you need `[formControl]="name"` in the template on the `<input>`.
|
||||
|
||||
|
||||
|
||||
|
@ -265,7 +265,7 @@ you need `[formControl]="name"` in the template on the `<input>`.
|
|||
|
||||
|
||||
|
||||
Disregard the `form-control` _CSS_ class. It belongs to the
|
||||
Disregard the `form-control` _CSS_ class. It belongs to the
|
||||
<a href="http://getbootstrap.com/" title="Bootstrap CSS">Bootstrap CSS library</a>,
|
||||
not Angular.
|
||||
It _styles_ the form but in no way impacts the logic of the form.
|
||||
|
@ -280,13 +280,13 @@ It _styles_ the form but in no way impacts the logic of the form.
|
|||
|
||||
## Import the _ReactiveFormsModule_
|
||||
|
||||
The HeroDetailComponent template uses `formControlName`
|
||||
The HeroDetailComponent template uses `formControlName`
|
||||
directive from the `ReactiveFormsModule`.
|
||||
|
||||
In this sample, you declare the `HeroDetailComponent` in the `AppModule`.
|
||||
Therefore, do the following three things in `app.module.ts`:
|
||||
|
||||
1. Use a JavaScript `import` statement to access
|
||||
1. Use a JavaScript `import` statement to access
|
||||
the `ReactiveFormsModule` and the `HeroDetailComponent`.
|
||||
1. Add `ReactiveFormsModule` to the `AppModule`'s `imports` list.
|
||||
1. Add `HeroDetailComponent` to the declarations array.
|
||||
|
@ -318,11 +318,11 @@ Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
|
|||
It may be helpful to read a brief description of the core form classes.
|
||||
|
||||
* [_AbstractControl_](api/forms/index/AbstractControl-class "API Reference: AbstractControl")
|
||||
is the abstract base class for the three concrete form control classes:
|
||||
is the abstract base class for the three concrete form control classes:
|
||||
`FormControl`, `FormGroup`, and `FormArray`.
|
||||
It provides their common behaviors and properties, some of which are _observable_.
|
||||
|
||||
* [_FormControl_](api/forms/index/FormControl-class "API Reference: FormControl")
|
||||
* [_FormControl_](api/forms/index/FormControl-class "API Reference: FormControl")
|
||||
tracks the value and validity status of an _individual_ form control.
|
||||
It corresponds to an HTML form control such as an input box or selector.
|
||||
|
||||
|
@ -349,11 +349,11 @@ Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
|
|||
|
||||
|
||||
|
||||
Now that everything is wired up, the browser should display something like this:
|
||||
Now that everything is wired up, the browser should display something like this:
|
||||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/just-formcontrol.png" width="400px" alt="Single FormControl"></img>
|
||||
<img src="content/images/guide/reactive-forms/just-formcontrol.png" width="400px" alt="Single FormControl"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -362,9 +362,9 @@ Now that everything is wired up, the browser should display something like this:
|
|||
|
||||
|
||||
## Add a FormGroup
|
||||
Usually, if you have multiple *FormControls*, you'll want to register
|
||||
Usually, if you have multiple *FormControls*, you'll want to register
|
||||
them within a parent `FormGroup`.
|
||||
This is simple to do. To add a `FormGroup`, add it to the imports section
|
||||
This is simple to do. To add a `FormGroup`, add it to the imports section
|
||||
of `hero-detail.component.ts`:
|
||||
|
||||
|
||||
|
@ -383,7 +383,7 @@ In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follo
|
|||
|
||||
|
||||
|
||||
Now that you've made changes in the class, they need to be reflected in the
|
||||
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.
|
||||
|
||||
|
||||
|
@ -393,26 +393,26 @@ template. Update `hero-detail.component.html` by replacing it with the following
|
|||
|
||||
|
||||
|
||||
Notice that now the single input is in a `form` element. The `novalidate`
|
||||
attribute in the `<form>` element prevents the browser
|
||||
from attempting native HTML validations.
|
||||
Notice that now the single input is in a `form` element. The `novalidate`
|
||||
attribute in the `<form>` element prevents the browser
|
||||
from attempting native HTML validations.
|
||||
|
||||
`formGroup` is a reactive form directive that takes an existing
|
||||
`FormGroup` instance and associates it with an HTML element.
|
||||
In this case, it associates the `FormGroup` you saved as
|
||||
`formGroup` is a reactive form directive that takes an existing
|
||||
`FormGroup` instance and associates it with an HTML element.
|
||||
In this case, it associates the `FormGroup` you saved as
|
||||
`heroForm` with the form element.
|
||||
|
||||
Because the class now has a `FormGroup`, you must update the template
|
||||
syntax for associating the input with the corresponding
|
||||
`FormControl` in the component class.
|
||||
Without a parent `FormGroup`,
|
||||
`[formControl]="name"` worked earlier because that directive
|
||||
can stand alone, that is, it works without being in a `FormGroup`.
|
||||
With a parent `FormGroup`, the `name` input needs the syntax
|
||||
`formControlName=name` in order to be associated
|
||||
with the correct `FormControl`
|
||||
in the class. This syntax tells Angular to look for the parent
|
||||
`FormGroup`, in this case `heroForm`, and then _inside_ that group
|
||||
Because the class now has a `FormGroup`, you must update the template
|
||||
syntax for associating the input with the corresponding
|
||||
`FormControl` in the component class.
|
||||
Without a parent `FormGroup`,
|
||||
`[formControl]="name"` worked earlier because that directive
|
||||
can stand alone, that is, it works without being in a `FormGroup`.
|
||||
With a parent `FormGroup`, the `name` input needs the syntax
|
||||
`formControlName=name` in order to be associated
|
||||
with the correct `FormControl`
|
||||
in the class. This syntax tells Angular to look for the parent
|
||||
`FormGroup`, in this case `heroForm`, and then _inside_ that group
|
||||
to look for a `FormControl` called `name`.
|
||||
|
||||
|
||||
|
@ -420,10 +420,10 @@ to look for a `FormControl` called `name`.
|
|||
|
||||
|
||||
|
||||
Disregard the `form-group` _CSS_ class. It belongs to the
|
||||
Disregard the `form-group` _CSS_ class. It belongs to the
|
||||
<a href="http://getbootstrap.com/" title="Bootstrap CSS">Bootstrap CSS library</a>,
|
||||
not Angular.
|
||||
Like the `form-control` class, it _styles_ the form
|
||||
Like the `form-control` class, it _styles_ the form
|
||||
but in no way impacts its logic.
|
||||
|
||||
|
||||
|
@ -432,8 +432,8 @@ but in no way impacts its logic.
|
|||
|
||||
|
||||
|
||||
The form looks great. But does it work?
|
||||
When the user enters a name, where does the value go?
|
||||
The form looks great. But does it work?
|
||||
When the user enters a name, where does the value go?
|
||||
|
||||
|
||||
{@a json}
|
||||
|
@ -442,7 +442,7 @@ When the user enters a name, where does the value go?
|
|||
## Taking a look at the form model
|
||||
|
||||
The value goes into the **_form model_** that backs the group's `FormControls`.
|
||||
To see the form model, add the following line after the
|
||||
To see the form model, add the following line after the
|
||||
closing `form` tag in the `hero-detail.component.html`:
|
||||
|
||||
|
||||
|
@ -457,20 +457,20 @@ Piping it through the `JsonPipe` renders the model as JSON in the browser:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/json-output.png" width="400px" alt="JSON output"></img>
|
||||
<img src="content/images/guide/reactive-forms/json-output.png" width="400px" alt="JSON output"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
The initial `name` property value is the empty string.
|
||||
The initial `name` property value is the empty string.
|
||||
Type into the _name_ input box and watch the keystokes appear in the JSON.
|
||||
|
||||
|
||||
|
||||
|
||||
Great! You have the basics of a form.
|
||||
Great! You have the basics of a form.
|
||||
|
||||
In real life apps, forms get big fast.
|
||||
In real life apps, forms get big fast.
|
||||
`FormBuilder` makes form development and maintenance easier.
|
||||
|
||||
|
||||
|
@ -481,8 +481,8 @@ In real life apps, forms get big fast.
|
|||
|
||||
## Introduction to _FormBuilder_
|
||||
|
||||
The `FormBuilder` class helps reduce repetition and
|
||||
clutter by handling details of control creation for you.
|
||||
The `FormBuilder` class helps reduce repetition and
|
||||
clutter by handling details of control creation for you.
|
||||
|
||||
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
|
||||
|
||||
|
@ -509,7 +509,7 @@ The revised `HeroDetailComponent` looks like this:
|
|||
|
||||
|
||||
`FormBuilder.group` is a factory method that creates a `FormGroup`.
|
||||
`FormBuilder.group` takes an object whose keys and values are `FormControl` names and their definitions.
|
||||
`FormBuilder.group` takes an object whose keys and values are `FormControl` names and their definitions.
|
||||
In this example, the `name` control is defined by its initial data value, an empty string.
|
||||
|
||||
Defining a group of controls in a single object makes for a compact, readable style.
|
||||
|
@ -520,8 +520,8 @@ It beats writing an equivalent series of `new FormControl(...)` statements.
|
|||
|
||||
|
||||
### Validators.required
|
||||
Though this guide doesn't go deeply into validations, here is one example that
|
||||
demonstrates the simplicity of using `Validators.required` in reactive forms.
|
||||
Though this guide doesn't go deeply into validations, here is one example that
|
||||
demonstrates the simplicity of using `Validators.required` in reactive forms.
|
||||
|
||||
First, import the `Validators` symbol.
|
||||
|
||||
|
@ -531,8 +531,8 @@ First, import the `Validators` symbol.
|
|||
|
||||
|
||||
|
||||
To make the `name` `FormControl` required, replace the `name`
|
||||
property in the `FormGroup` with an array.
|
||||
To make the `name` `FormControl` required, replace the `name`
|
||||
property in the `FormGroup` with an array.
|
||||
The first item is the initial value for `name`;
|
||||
the second is the required validator, `Validators.required`.
|
||||
|
||||
|
@ -548,7 +548,7 @@ the second is the required validator, `Validators.required`.
|
|||
|
||||
|
||||
Reactive validators are simple, composable functions.
|
||||
Configuring validation is harder in template-driven forms where you must wrap validators in a directive.
|
||||
Configuring validation is harder in template-driven forms where you must wrap validators in a directive.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -567,29 +567,29 @@ The browser displays the following:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/validators-json-output.png" width="400px" alt="Single FormControl"></img>
|
||||
<img src="content/images/guide/reactive-forms/validators-json-output.png" width="400px" alt="Single FormControl"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
`Validators.required` is working. The status is `INVALID` because the input box has no value.
|
||||
Type into the input box to see the status change from `INVALID` to `VALID`.
|
||||
Type into the input box to see the status change from `INVALID` to `VALID`.
|
||||
|
||||
In a real app, you'd replace the diagnosic message with a user-friendly experience.
|
||||
|
||||
|
||||
Using `Validators.required` is optional for the rest of the guide.
|
||||
Using `Validators.required` is optional for the rest of the guide.
|
||||
It remains in each of the following examples with the same configuration.
|
||||
|
||||
For more on validating Angular forms, see the
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
|
||||
|
||||
### More FormControls
|
||||
A hero has more than a name.
|
||||
A hero has an address, a super power and sometimes a sidekick too.
|
||||
A hero has more than a name.
|
||||
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`.
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
@ -598,7 +598,7 @@ the `<option>` elements with states. So import `states` from `data-model.ts`.
|
|||
|
||||
|
||||
|
||||
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
|
||||
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
|
||||
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
@ -607,7 +607,7 @@ Declare the `states` property and add some address `FormControls` to the `heroFo
|
|||
|
||||
|
||||
|
||||
Then add corresponding markup in `hero-detail.component.html`
|
||||
Then add corresponding markup in `hero-detail.component.html`
|
||||
within the `form` element.
|
||||
|
||||
|
||||
|
@ -621,11 +621,11 @@ within the `form` element.
|
|||
|
||||
|
||||
|
||||
*Reminder*: Ignore the many mentions of `form-group`,
|
||||
*Reminder*: Ignore the many mentions of `form-group`,
|
||||
`form-control`, `center-block`, and `checkbox` in this markup.
|
||||
Those are _bootstrap_ CSS classes that Angular itself ignores.
|
||||
Pay attention to the `formGroupName` and `formControlName` attributes.
|
||||
They are the Angular directives that bind the HTML controls to the
|
||||
They are the Angular directives that bind the HTML controls to the
|
||||
Angular `FormGroup` and `FormControl` properties in the component class.
|
||||
|
||||
|
||||
|
@ -633,19 +633,19 @@ Angular `FormGroup` and `FormControl` properties in the component class.
|
|||
|
||||
|
||||
|
||||
The revised template includes more text inputs, a select box for the `state`, radio buttons for the `power`,
|
||||
and a checkbox for the `sidekick`.
|
||||
The revised template includes more text inputs, a select box for the `state`, radio buttons for the `power`,
|
||||
and a checkbox for the `sidekick`.
|
||||
|
||||
You must bind the option's value property with `[value]="state"`.
|
||||
You must bind the option's value property with `[value]="state"`.
|
||||
If you do not bind the value, the select shows the first option from the data model.
|
||||
|
||||
The component _class_ defines control properties without regard for their representation in the template.
|
||||
You define the `state`, `power`, and `sidekick` controls the same way you defined the `name` control.
|
||||
You tie these controls to the template HTML elements in the same way,
|
||||
specifiying the `FormControl` name with the `formControlName` directive.
|
||||
You tie these controls to the template HTML elements in the same way,
|
||||
specifiying the `FormControl` name with the `formControlName` directive.
|
||||
|
||||
See the API reference for more information about
|
||||
[radio buttons](api/forms/index/RadioControlValueAccessor-directive "API: RadioControlValueAccessor"),
|
||||
See the API reference for more information about
|
||||
[radio buttons](api/forms/index/RadioControlValueAccessor-directive "API: RadioControlValueAccessor"),
|
||||
[selects](api/forms/index/SelectControlValueAccessor-directive "API: SelectControlValueAccessor"), and
|
||||
[checkboxes](api/forms/index/CheckboxControlValueAccessor-directive "API: CheckboxControlValueAccessor").
|
||||
|
||||
|
@ -656,12 +656,12 @@ See the API reference for more information about
|
|||
|
||||
### Nested FormGroups
|
||||
|
||||
This form is getting big and unwieldy. You can group some of the related `FormControls`
|
||||
into a nested `FormGroup`. The `street`, `city`, `state`, and `zip` are properties
|
||||
This form is getting big and unwieldy. You can group some of the related `FormControls`
|
||||
into a nested `FormGroup`. The `street`, `city`, `state`, and `zip` are properties
|
||||
that would make a good _address_ `FormGroup`.
|
||||
Nesting groups and controls in this way allows you to
|
||||
mirror the hierarchical structure of the data model
|
||||
and helps track validation and state for related sets of controls.
|
||||
Nesting groups and controls in this way allows you to
|
||||
mirror the hierarchical structure of the data model
|
||||
and helps track validation and state for related sets of controls.
|
||||
|
||||
You used the `FormBuilder` to create one `FormGroup` in this component called `heroForm`.
|
||||
Let that be the parent `FormGroup`.
|
||||
|
@ -674,14 +674,14 @@ assign the result to a new `address` property of the parent `FormGroup`.
|
|||
|
||||
|
||||
|
||||
You’ve changed the structure of the form controls in the component class;
|
||||
You’ve changed the structure of the form controls in the component class;
|
||||
you must make corresponding adjustments to the component template.
|
||||
|
||||
In `hero-detail.component.html`, wrap the address-related `FormControls` in a `div`.
|
||||
Add a `formGroupName` directive to the `div` and bind it to `"address"`.
|
||||
That's the property of the _address_ child `FormGroup` within the parent `FormGroup` called `heroForm`.
|
||||
|
||||
To make this change visually obvious, slip in an `<h4>` header near the top with the text, _Secret Lair_.
|
||||
To make this change visually obvious, slip in an `<h4>` header near the top with the text, _Secret Lair_.
|
||||
The new _address_ HTML looks like this:
|
||||
|
||||
|
||||
|
@ -692,16 +692,16 @@ The new _address_ HTML looks like this:
|
|||
|
||||
|
||||
After these changes, the JSON output in the browser shows the revised _form model_
|
||||
with the nested address `FormGroup`:
|
||||
with the nested address `FormGroup`:
|
||||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/address-group.png" width="400px" alt="JSON output"></img>
|
||||
<img src="content/images/guide/reactive-forms/address-group.png" width="400px" alt="JSON output"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
Great! You’ve made a group and you can see that the template
|
||||
Great! You’ve made a group and you can see that the template
|
||||
and the form model are talking to one another.
|
||||
|
||||
|
||||
|
@ -714,9 +714,9 @@ At the moment, you're dumping the entire form model onto the page.
|
|||
Sometimes you're interested only in the state of one particular `FormControl`.
|
||||
|
||||
You can inspect an individual `FormControl` within a form by extracting it with the `.get()` method.
|
||||
You can do this _within_ the component class or display it on the
|
||||
page by adding the following to the template,
|
||||
immediately after the `{{form.value | json}}` interpolation as follows:
|
||||
You can do this _within_ the component class or display it on the
|
||||
page by adding the following to the template,
|
||||
immediately after the `{{form.value | json}}` interpolation as follows:
|
||||
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" title="src/app/hero-detail.component.html" linenums="false">
|
||||
|
@ -734,7 +734,7 @@ To get the state of a `FormControl` that’s inside a `FormGroup`, use dot notat
|
|||
|
||||
|
||||
|
||||
You can use this technique to display _any_ property of a `FormControl`
|
||||
You can use this technique to display _any_ property of a `FormControl`
|
||||
such as one of the following:
|
||||
|
||||
<style>
|
||||
|
@ -788,7 +788,7 @@ such as one of the following:
|
|||
<td>
|
||||
|
||||
|
||||
the validity of a `FormControl`. Possible values: `VALID`,
|
||||
the validity of a `FormControl`. Possible values: `VALID`,
|
||||
`INVALID`, `PENDING`, or `DISABLED`.
|
||||
</td>
|
||||
|
||||
|
@ -820,7 +820,7 @@ such as one of the following:
|
|||
|
||||
`true` if the control user has not yet entered the HTML control
|
||||
and triggered its blur event. Its opposite is `myControl.touched`.
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
@ -829,13 +829,13 @@ such as one of the following:
|
|||
|
||||
|
||||
|
||||
Learn about other `FormControl` properties in the
|
||||
Learn about other `FormControl` properties in the
|
||||
[_AbstractControl_](api/forms/index/AbstractControl-class) API reference.
|
||||
|
||||
One common reason for inspecting `FormControl` properties is to
|
||||
make sure the user entered valid values.
|
||||
Read more about validating Angular forms in the
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
One common reason for inspecting `FormControl` properties is to
|
||||
make sure the user entered valid values.
|
||||
Read more about validating Angular forms in the
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
|
||||
|
||||
|
||||
|
@ -856,7 +856,7 @@ The `FormControl` structure is the **_form model_**.
|
|||
The component must copy the hero values in the _data model_ into the _form model_.
|
||||
There are two important implications:
|
||||
|
||||
1. The developer must understand how the properties of the _data model_
|
||||
1. The developer must understand how the properties of the _data model_
|
||||
map to the properties of the _form model_.
|
||||
|
||||
2. User changes flow from the DOM elements to the _form model_, not to the _data model_.
|
||||
|
@ -917,8 +917,8 @@ Also be sure to update the import from `data-model` so you can reference the `He
|
|||
|
||||
|
||||
## Populate the form model with _setValue_ and _patchValue_
|
||||
Previously you created a control and initialized its value at the same time.
|
||||
You can also initialize or reset the values _later_ with the
|
||||
Previously you created a control and initialized its value at the same time.
|
||||
You can also initialize or reset the values _later_ with the
|
||||
`setValue` and `patchValue` methods.
|
||||
|
||||
### _setValue_
|
||||
|
@ -934,13 +934,13 @@ by passing in a data object whose properties exactly match the _form model_ behi
|
|||
|
||||
The `setValue` method checks the data object thoroughly before assigning any form control values.
|
||||
|
||||
It will not accept a data object that doesn't match the FormGroup structure or is
|
||||
missing values for any control in the group. This way, it can return helpful
|
||||
error messages if you have a typo or if you've nested controls incorrectly.
|
||||
It will not accept a data object that doesn't match the FormGroup structure or is
|
||||
missing values for any control in the group. This way, it can return helpful
|
||||
error messages if you have a typo or if you've nested controls incorrectly.
|
||||
`patchValue` will fail silently.
|
||||
|
||||
On the other hand,`setValue` will catch
|
||||
the error and report it clearly.
|
||||
On the other hand,`setValue` will catch
|
||||
the error and report it clearly.
|
||||
|
||||
Notice that you can _almost_ use the entire `hero` as the argument to `setValue`
|
||||
because its shape is similar to the component's `FormGroup` structure.
|
||||
|
@ -956,7 +956,7 @@ This explains the conditional setting of the `address` property in the data obje
|
|||
|
||||
### _patchValue_
|
||||
With **`patchValue`**, you can assign values to specific controls in a `FormGroup`
|
||||
by supplying an object of key/value pairs for just the controls of interest.
|
||||
by supplying an object of key/value pairs for just the controls of interest.
|
||||
|
||||
This example sets only the form's `name` control.
|
||||
|
||||
|
@ -967,7 +967,7 @@ This example sets only the form's `name` control.
|
|||
|
||||
|
||||
With **`patchValue`** you have more flexibility to cope with wildly divergent data and form models.
|
||||
But unlike `setValue`, `patchValue` cannot check for missing control
|
||||
But unlike `setValue`, `patchValue` cannot check for missing control
|
||||
values and does not throw helpful errors.
|
||||
|
||||
### When to set form model values (_ngOnChanges_)
|
||||
|
@ -987,7 +987,7 @@ by binding to its `hero` input property.
|
|||
|
||||
|
||||
|
||||
In this approach, the value of `hero` in the `HeroDetailComponent` changes
|
||||
In this approach, the value of `hero` in the `HeroDetailComponent` changes
|
||||
every time the user selects a new hero.
|
||||
You should call _setValue_ in the [ngOnChanges](guide/lifecycle-hooks#onchanges)
|
||||
hook, which Angular calls whenever the input `hero` property changes
|
||||
|
@ -1021,8 +1021,8 @@ Add the `ngOnChanges` method to the class as follows:
|
|||
|
||||
### _reset_ the form flags
|
||||
|
||||
You should reset the form when the hero changes so that
|
||||
control values from the previous hero are cleared and
|
||||
You should reset the form when the hero changes so that
|
||||
control values from the previous hero are cleared and
|
||||
status flags are restored to the _pristine_ state.
|
||||
You could call `reset` at the top of `ngOnChanges` like this.
|
||||
|
||||
|
@ -1032,8 +1032,8 @@ You could call `reset` at the top of `ngOnChanges` like this.
|
|||
|
||||
|
||||
|
||||
The `reset` method has an optional `state` value so you can reset the flags _and_ the control values at the same.
|
||||
Internally, `reset` passes the argument to `setValue`.
|
||||
The `reset` method has an optional `state` value so you can reset the flags _and_ the control values at the same.
|
||||
Internally, `reset` passes the argument to `setValue`.
|
||||
A little refactoring and `ngOnChanges` becomes this:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" title="src/app/hero-detail.component.ts (ngOnchanges - revised)" linenums="false">
|
||||
|
@ -1052,16 +1052,16 @@ Together they look a bit like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/hero-list.png" width="420px" alt="HeroListComponent"></img>
|
||||
<img src="content/images/guide/reactive-forms/hero-list.png" width="420px" alt="HeroListComponent"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
The `HeroListComponent` uses an injected `HeroService` to retrieve heroes from the server
|
||||
and then presents those heroes to the user as a series of buttons.
|
||||
The `HeroService` emulates an HTTP service.
|
||||
It returns an `Observable` of heroes that resolves after a short delay,
|
||||
both to simulate network latency and to indicate visually
|
||||
The `HeroService` emulates an HTTP service.
|
||||
It returns an `Observable` of heroes that resolves after a short delay,
|
||||
both to simulate network latency and to indicate visually
|
||||
the necessarily asynchronous nature of the application.
|
||||
|
||||
When the user clicks on a hero,
|
||||
|
@ -1076,11 +1076,11 @@ The remaining `HeroListComponent` and `HeroService` implementation details are n
|
|||
The techniques involved are covered elsewhere in the documentation, including the _Tour of Heroes_
|
||||
[here](tutorial/toh-pt3 "ToH: Multiple Components") and [here](tutorial/toh-pt4 "ToH: Services").
|
||||
|
||||
If you're coding along with the steps in this reactive forms tutorial,
|
||||
create the pertinent files based on the
|
||||
[source code displayed below](guide/reactive-forms#source-code "Reactive Forms source code").
|
||||
If you're coding along with the steps in this reactive forms tutorial,
|
||||
create the pertinent files based on the
|
||||
[source code displayed below](guide/reactive-forms#source-code "Reactive Forms source code").
|
||||
Notice that `hero-list.component.ts` imports `Observable` and `finally` while `hero.service.ts` imports `Observable`, `of`,
|
||||
and `delay` from `rxjs`.
|
||||
and `delay` from `rxjs`.
|
||||
Then return here to learn about _form array_ properties.
|
||||
|
||||
|
||||
|
@ -1116,7 +1116,7 @@ In this guide, you define a `FormArray` for `Hero.addresses` and
|
|||
let the user add or modify addresses (removing addresses is your homework).
|
||||
|
||||
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" title="src/app/hero-detail-7.component.ts" linenums="false">
|
||||
|
||||
|
@ -1143,7 +1143,7 @@ Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` de
|
|||
Changing the form control name from `address` to `secretLairs` drives home an important point:
|
||||
the _form model_ doesn't have to match the _data model_.
|
||||
|
||||
Obviously there has to be a relationship between the two.
|
||||
Obviously there has to be a relationship between the two.
|
||||
But it can be anything that makes sense within the application domain.
|
||||
|
||||
_Presentation_ requirements often differ from _data_ requirements.
|
||||
|
@ -1187,21 +1187,21 @@ Wrap the expression in a `secretLairs` convenience property for clarity and re-u
|
|||
|
||||
|
||||
|
||||
### Display the _FormArray_
|
||||
### Display the _FormArray_
|
||||
|
||||
The current HTML template displays a single _address_ `FormGroup`.
|
||||
Revise it to display zero, one, or more of the hero's _address_ `FormGroups`.
|
||||
|
||||
This is mostly a matter of wrapping the previous template HTML for an address in a `<div>` and
|
||||
This is mostly a matter of wrapping the previous template HTML for an address in a `<div>` and
|
||||
repeating that `<div>` with `*ngFor`.
|
||||
|
||||
The trick lies in knowing how to write the `*ngFor`. There are three key points:
|
||||
|
||||
1. Add another wrapping `<div>`, around the `<div>` with `*ngFor`, and
|
||||
1. Add another wrapping `<div>`, around the `<div>` with `*ngFor`, and
|
||||
set its `formArrayName` directive to `"secretLairs"`.
|
||||
This step establishes the _secretLairs_ `FormArray` as the context for form controls in the inner, repeated HTML template.
|
||||
This step establishes the _secretLairs_ `FormArray` as the context for form controls in the inner, repeated HTML template.
|
||||
|
||||
1. The source of the repeated items is the `FormArray.controls`, not the `FormArray` itself.
|
||||
1. The source of the repeated items is the `FormArray.controls`, not the `FormArray` itself.
|
||||
Each control is an _address_ `FormGroup`, exactly what the previous (now repeated) template HTML expected.
|
||||
|
||||
1. Each repeated `FormGroup` needs a unique `formGroupName` which must be the index of the `FormGroup` in the `FormArray`.
|
||||
|
@ -1225,7 +1225,7 @@ Here's the complete template for the _secret lairs_ section:
|
|||
|
||||
### Add a new lair to the _FormArray_
|
||||
|
||||
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" title="src/app/hero-detail.component.ts (addLair method)" linenums="false">
|
||||
|
||||
|
@ -1246,7 +1246,7 @@ Place a button on the form so the user can add a new _secret lair_ and wire it t
|
|||
|
||||
|
||||
|
||||
Be sure to **add the `type="button"` attribute**.
|
||||
Be sure to **add the `type="button"` attribute**.
|
||||
In fact, you should always specify a button's `type`.
|
||||
Without an explict type, the button type defaults to "submit".
|
||||
When you later add a _form submit_ action, every "submit" button triggers the submit action which
|
||||
|
@ -1265,12 +1265,12 @@ Back in the browser, select the hero named "Magneta".
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array"></img>
|
||||
<img src="content/images/guide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
Click the "_Add a Secret Lair_" button.
|
||||
Click the "_Add a Secret Lair_" button.
|
||||
A new address section appears. Well done!
|
||||
|
||||
### Remove a lair
|
||||
|
@ -1289,7 +1289,7 @@ Angular calls `ngOnChanges` when the user picks a hero in the parent `HeroListCo
|
|||
Picking a hero changes the `HeroDetailComponent.hero` input property.
|
||||
|
||||
Angular does _not_ call `ngOnChanges` when the user modifies the hero's _name_ or _secret lairs_.
|
||||
Fortunately, you can learn about such changes by subscribing to one of the form control properties
|
||||
Fortunately, you can learn about such changes by subscribing to one of the form control properties
|
||||
that raises a change event.
|
||||
|
||||
These are properties, such as `valueChanges`, that return an RxJS `Observable`.
|
||||
|
@ -1326,7 +1326,7 @@ You should see a new name in the log after each keystroke.
|
|||
### When to use it
|
||||
|
||||
An interpolation binding is the easier way to _display_ a name change.
|
||||
Subscribing to an observable form control property is handy for triggering
|
||||
Subscribing to an observable form control property is handy for triggering
|
||||
application logic _within_ the component class.
|
||||
|
||||
|
||||
|
@ -1343,7 +1343,7 @@ After you implement both features in this section, the form will look like this:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons"></img>
|
||||
<img src="content/images/guide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1361,7 +1361,7 @@ to a save method on the injected `HeroService`.
|
|||
|
||||
This original `hero` had the pre-save values. The user's changes are still in the _form model_.
|
||||
So you create a new `hero` from a combination of original hero values (the `hero.id`)
|
||||
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" title="src/app/hero-detail.component.ts (prepareSaveHero)" linenums="false">
|
||||
|
@ -1377,7 +1377,7 @@ and deep copies of the changed form model values, using the `prepareSaveHero` he
|
|||
**Address deep copy**
|
||||
|
||||
Had you assigned the `formModel.secretLairs` to `saveHero.addresses` (see line commented out),
|
||||
the addresses in `saveHero.addresses` array would be the same objects
|
||||
the addresses in `saveHero.addresses` array would be the same objects
|
||||
as the lairs in the `formModel.secretLairs`.
|
||||
A user's subsequent changes to a lair street would mutate an address street in the `saveHero`.
|
||||
|
||||
|
|
|
@ -85,13 +85,13 @@ attacker-controlled data enters the DOM, expect security vulnerabilities.
|
|||
### Angular’s cross-site scripting security model
|
||||
|
||||
To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value
|
||||
is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation,
|
||||
is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation,
|
||||
Angular sanitizes and escapes untrusted values.
|
||||
|
||||
_Angular templates are the same as executable code_: HTML, attributes, and binding expressions
|
||||
(but not the values bound) in templates are trusted to be safe. This means that applications must
|
||||
prevent values that an attacker can control from ever making it into the source code of a
|
||||
template. Never generate template source code by concatenating user input and templates.
|
||||
template. Never generate template source code by concatenating user input and templates.
|
||||
To prevent these vulnerabilities, use
|
||||
the [offline template compiler](guide/security#offline-template-compiler), also known as _template injection_.
|
||||
|
||||
|
@ -143,7 +143,7 @@ tag but keeps safe content such as the text content of the `<script>` tag and th
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'></img>
|
||||
<img src='content/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -159,7 +159,7 @@ templates where possible.
|
|||
|
||||
Content Security Policy (CSP) is a defense-in-depth
|
||||
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
|
||||
`Content-Security-Policy` HTTP header. Read more about content security policy at
|
||||
`Content-Security-Policy` HTTP header. Read more about content security policy at
|
||||
[An Introduction to Content Security Policy](http://www.html5rocks.com/en/tutorials/security/content-security-policy/)
|
||||
on the HTML5Rocks website.
|
||||
|
||||
|
@ -172,15 +172,15 @@ on the HTML5Rocks website.
|
|||
The offline template compiler prevents a whole class of vulnerabilities called template injection,
|
||||
and greatly improves application performance. Use the offline template compiler in production
|
||||
deployments; don't dynamically generate templates. Angular trusts template code, so generating
|
||||
templates, in particular templates containing user data, circumvents Angular's built-in protections.
|
||||
For information about dynamically constructing forms in a safe way, see the
|
||||
templates, in particular templates containing user data, circumvents Angular's built-in protections.
|
||||
For information about dynamically constructing forms in a safe way, see the
|
||||
[Dynamic Forms](cookbook/dynamic-form) cookbook page.
|
||||
|
||||
### Server-side XSS protection
|
||||
|
||||
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
|
||||
Angular application is the same as injecting executable code into the
|
||||
application: it gives the attacker full control over the application. To prevent this,
|
||||
application: it gives the attacker full control over the application. To prevent this,
|
||||
use a templating language that automatically escapes values to prevent XSS vulnerabilities on
|
||||
the server. Don't generate Angular templates on the server side using a templating language; doing this
|
||||
carries a high risk of introducing template-injection vulnerabilities.
|
||||
|
@ -194,10 +194,10 @@ carries a high risk of introducing template-injection vulnerabilities.
|
|||
|
||||
|
||||
Sometimes applications genuinely need to include executable code, display an `<iframe>` from some
|
||||
URL, or construct potentially dangerous URLs. To prevent automatic sanitization in any of these
|
||||
situations, you can tell Angular that you inspected a value, checked how it was generated, and made
|
||||
sure it will always be secure. But *be careful*. If you trust a value that might be malicious, you
|
||||
are introducing a security vulnerability into your application. If in doubt, find a professional
|
||||
URL, or construct potentially dangerous URLs. To prevent automatic sanitization in any of these
|
||||
situations, you can tell Angular that you inspected a value, checked how it was generated, and made
|
||||
sure it will always be secure. But *be careful*. If you trust a value that might be malicious, you
|
||||
are introducing a security vulnerability into your application. If in doubt, find a professional
|
||||
security reviewer.
|
||||
|
||||
To mark a value as trusted, inject `DomSanitizer` and call one of the
|
||||
|
@ -232,7 +232,7 @@ this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` cal
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'></img>
|
||||
<img src='content/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -240,7 +240,7 @@ this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` cal
|
|||
If you need to convert user input into a trusted value, use a
|
||||
controller method. The following template allows users to enter a YouTube video ID and load the
|
||||
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
|
||||
context, because an untrusted source can, for example, smuggle in file downloads that unsuspecting users
|
||||
context, because an untrusted source can, for example, smuggle in file downloads that unsuspecting users
|
||||
could execute. So call a method on the controller to construct a trusted video URL, which causes
|
||||
Angular to allow binding into `<iframe src>`:
|
||||
|
||||
|
@ -265,7 +265,7 @@ Angular to allow binding into `<iframe src>`:
|
|||
|
||||
|
||||
Angular has built-in support to help prevent two common HTTP vulnerabilities, cross-site request
|
||||
forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily
|
||||
forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily
|
||||
on the server side, but Angular provides helpers to make integration on the client side easier.
|
||||
|
||||
|
||||
|
@ -276,8 +276,8 @@ on the server side, but Angular provides helpers to make integration on the clie
|
|||
|
||||
|
||||
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
|
||||
a different web page (such as `evil.com`) with malignant code that secretly sends a malicious request
|
||||
to the application's web server (such as `example-bank.com`).
|
||||
a different web page (such as `evil.com`) with malignant code that secretly sends a malicious request
|
||||
to the application's web server (such as `example-bank.com`).
|
||||
|
||||
Assume the user is logged into the application at `example-bank.com`.
|
||||
The user opens an email and clicks a link to `evil.com`, which opens in a new tab.
|
||||
|
@ -286,11 +286,11 @@ The `evil.com` page immediately sends a malicious request to `example-bank.com`.
|
|||
Perhaps it's a request to transfer money from the user's account to the attacker's account.
|
||||
The browser automatically sends the `example-bank.com` cookies (including the authentication cookie) with this request.
|
||||
|
||||
If the `example-bank.com` server lacks XSRF protection, it can't tell the difference between a legitimate
|
||||
If the `example-bank.com` server lacks XSRF protection, it can't tell the difference between a legitimate
|
||||
request from the application and the forged request from `evil.com`.
|
||||
|
||||
To prevent this, the application must ensure that a user request originates from the real
|
||||
application, not from a different site.
|
||||
application, not from a different site.
|
||||
The server and client must cooperate to thwart this attack.
|
||||
|
||||
In a common anti-XSRF technique, the application server sends a randomly
|
||||
|
@ -298,14 +298,14 @@ generated authentication token in a cookie.
|
|||
The client code reads the cookie and adds a custom request header with the token in all subsequent requests.
|
||||
The server compares the received cookie value to the request header value and rejects the request if the values are missing or don't match.
|
||||
|
||||
This technique is effective because all browsers implement the _same origin policy_. Only code from the website
|
||||
This technique is effective because all browsers implement the _same origin policy_. Only code from the website
|
||||
on which cookies are set can read the cookies from that site and set custom headers on requests to that site.
|
||||
That means only your application can read this cookie token and set the custom header. The malicious code on `evil.com` can't.
|
||||
|
||||
Angular's `http` has built-in support for the client-side half of this technique in its `XSRFStrategy`.
|
||||
Angular's `http` has built-in support for the client-side half of this technique in its `XSRFStrategy`.
|
||||
The default `CookieXSRFStrategy` is turned on automatically.
|
||||
Before sending an HTTP request, the `CookieXSRFStrategy` looks for a cookie called `XSRF-TOKEN` and
|
||||
sets a header named `X-XSRF-TOKEN` with the value of that cookie.
|
||||
Before sending an HTTP request, the `CookieXSRFStrategy` looks for a cookie called `XSRF-TOKEN` and
|
||||
sets a header named `X-XSRF-TOKEN` with the value of that cookie.
|
||||
|
||||
The server must do its part by setting the
|
||||
initial `XSRF-TOKEN` cookie and confirming that each subsequent state-modifying request
|
||||
|
@ -336,10 +336,10 @@ Or you can implement and provide an entirely custom `XSRFStrategy`:
|
|||
For information about CSRF at the Open Web Application Security Project (OWASP), see
|
||||
<a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29">Cross-Site Request Forgery (CSRF)</a> and
|
||||
<a href="https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet">Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet</a>.
|
||||
The Stanford University paper
|
||||
The Stanford University paper
|
||||
<a href="https://seclab.stanford.edu/websec/csrf/csrf.pdf">Robust Defenses for Cross-Site Request Forgery</a> is a rich source of detail.
|
||||
|
||||
See also Dave Smith's easy-to-understand
|
||||
See also Dave Smith's easy-to-understand
|
||||
<a href="https://www.youtube.com/watch?v=9inczw6qtpY" title="Cross Site Request Funkery Securing Your Angular Apps From Evil Doers">talk on XSRF at AngularConnect 2016</a>.
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ See the <live-example name="set-document-title"></live-example>.
|
|||
</td>
|
||||
|
||||
<td>
|
||||
<img src='assets/images/plunker/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"></img> <br></br> <img src='assets/images/plunker/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"></img>
|
||||
<img src='content/images/plunker/plunker-switch-to-editor-button.png' width="200px" height="70px" alt="pop out the window" align="right"></img> <br></br> <img src='content/images/plunker/plunker-separate-window-button.png' width="200px" height="47px" alt="pop out the window" align="right"></img>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
@ -75,7 +75,7 @@ The [Title](api/platform-browser/index/Title-class) service is a simple class th
|
|||
for getting and setting the current HTML document title:
|
||||
|
||||
* `getTitle() : string`—Gets the title of the current HTML document.
|
||||
* `setTitle( newTitle : string )`—Sets the title of the current HTML document.
|
||||
* `setTitle( newTitle : string )`—Sets the title of the current HTML document.
|
||||
|
||||
You can inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
|
||||
|
||||
|
@ -89,7 +89,7 @@ You can inject the `Title` service into the root `AppComponent` and expose a bin
|
|||
Bind that method to three anchor tags and voilà!
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/set-document-title/set-title-anim.gif" alt="Set title"></img>
|
||||
<img src="content/images/guide/set-document-title/set-title-anim.gif" alt="Set title"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -126,8 +126,8 @@ a location you reserve for configuring the runtime Angular environment.
|
|||
That's exactly what you're doing.
|
||||
The `Title` service is part of the Angular *browser platform*.
|
||||
If you bootstrap your application into a different platform,
|
||||
you'll have to provide a different `Title` service that understands
|
||||
you'll have to provide a different `Title` service that understands
|
||||
the concept of a "document title" for that specific platform.
|
||||
Ideally, the application itself neither knows nor cares about the runtime environment.
|
||||
|
||||
[Back to top](guide/set-document-title#top)
|
||||
[Back to top](guide/set-document-title#top)
|
||||
|
|
|
@ -16,7 +16,7 @@ Angular has a powerful template engine that lets us easily manipulate the DOM st
|
|||
|
||||
|
||||
|
||||
This guide looks at how Angular manipulates the DOM with **structural directives** and
|
||||
This guide looks at how Angular manipulates the DOM with **structural directives** and
|
||||
how you can write your own structural directives to do the same thing.
|
||||
|
||||
### Table of contents
|
||||
|
@ -50,10 +50,10 @@ Structural directives are responsible for HTML layout.
|
|||
They shape or reshape the DOM's _structure_, typically by adding, removing, or manipulating
|
||||
elements.
|
||||
|
||||
As with other directives, you apply a structural directive to a _host element_.
|
||||
As with other directives, you apply a structural directive to a _host element_.
|
||||
The directive then does whatever it's supposed to do with that host element and its descendents.
|
||||
|
||||
Structural directives are easy to recognize.
|
||||
Structural directives are easy to recognize.
|
||||
An asterisk (*) precedes the directive attribute name as in this example.
|
||||
|
||||
|
||||
|
@ -69,12 +69,12 @@ You'll learn in this guide that the [asterisk (*) is a convenience notation](gui
|
|||
and the string is a [_microsyntax_](guide/structural-directives#microsyntax) rather than the usual
|
||||
[template expression](guide/template-syntax#template-expressions).
|
||||
Angular desugars this notation into a marked-up `<ng-template>` that surrounds the
|
||||
host element and its descendents.
|
||||
host element and its descendents.
|
||||
Each structural directive does something different with that template.
|
||||
|
||||
Three of the common, built-in structural directives—[NgIf](guide/template-syntax#ngIf),
|
||||
[NgFor](guide/template-syntax#ngFor), and [NgSwitch...](guide/template-syntax#ngSwitch)—are
|
||||
described in the [_Template Syntax_](guide/template-syntax) guide and seen in samples throughout the Angular documentation.
|
||||
Three of the common, built-in structural directives—[NgIf](guide/template-syntax#ngIf),
|
||||
[NgFor](guide/template-syntax#ngFor), and [NgSwitch...](guide/template-syntax#ngSwitch)—are
|
||||
described in the [_Template Syntax_](guide/template-syntax) guide and seen in samples throughout the Angular documentation.
|
||||
Here's an example of them in a template:
|
||||
|
||||
|
||||
|
@ -100,8 +100,8 @@ and how to [write your own](guide/structural-directives#unless) structural direc
|
|||
|
||||
Throughout this guide, you'll see a directive spelled in both _UpperCamelCase_ and _lowerCamelCase_.
|
||||
Already you've seen `NgIf` and `ngIf`.
|
||||
There's a reason. `NgIf` refers to the directive _class_;
|
||||
`ngIf` refers to the directive's _attribute name_.
|
||||
There's a reason. `NgIf` refers to the directive _class_;
|
||||
`ngIf` refers to the directive's _attribute name_.
|
||||
|
||||
A directive _class_ is spelled in _UpperCamelCase_ (`NgIf`).
|
||||
A directive's _attribute name_ is spelled in _lowerCamelCase_ (`ngIf`).
|
||||
|
@ -122,7 +122,7 @@ There are two other kinds of Angular directives, described extensively elsewhere
|
|||
(1) components and (2) attribute directives.
|
||||
|
||||
A *component* manages a region of HTML in the manner of a native HTML element.
|
||||
Technically it's a directive with a template.
|
||||
Technically it's a directive with a template.
|
||||
|
||||
An [*attribute* directive](guide/attribute-directives) changes the appearance or behavior
|
||||
of an element, component, or another directive.
|
||||
|
@ -158,12 +158,12 @@ Confirm that fact using browser developer tools to inspect the DOM.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM"></img>
|
||||
<img src='content/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
The top paragraph is in the DOM. The bottom, disused paragraph is not;
|
||||
The top paragraph is in the DOM. The bottom, disused paragraph is not;
|
||||
in its place is a comment about "bindings" (more about that [later](guide/structural-directives#asterisk)).
|
||||
|
||||
When the condition is false, `NgIf` removes its host element from the DOM,
|
||||
|
@ -182,16 +182,16 @@ A directive could hide the unwanted paragraph instead by setting its `display` s
|
|||
|
||||
|
||||
|
||||
While invisible, the element remains in the DOM.
|
||||
While invisible, the element remains in the DOM.
|
||||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM"></img>
|
||||
<img src='content/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
The difference between hiding and removing doesn't matter for a simple paragraph.
|
||||
The difference between hiding and removing doesn't matter for a simple paragraph.
|
||||
It does matter when the host element is attached to a resource intensive component.
|
||||
Such a component's behavior continues even when hidden.
|
||||
The component stays attached to its DOM element. It keeps listening to events.
|
||||
|
@ -206,12 +206,12 @@ The component's previous state is preserved and ready to display.
|
|||
The component doesn't re-initialize—an operation that could be expensive.
|
||||
So hiding and showing is sometimes the right thing to do.
|
||||
|
||||
But in the absence of a compelling reason to keep them around,
|
||||
But in the absence of a compelling reason to keep them around,
|
||||
your preference should be to remove DOM elements that the user can't see
|
||||
and recover the unused resources with a structural directive like `NgIf` .
|
||||
|
||||
**These same considerations apply to every structural directive, whether built-in or custom.**
|
||||
Before applying a structural directive, you might want to pause for a moment
|
||||
Before applying a structural directive, you might want to pause for a moment
|
||||
to consider the consequences of adding and removing elements and of creating and destroying components.
|
||||
|
||||
|
||||
|
@ -256,17 +256,17 @@ Then it translates the template _attribute_ into a `<ng-template>` _element_, wr
|
|||
* The `*ngIf` directive moved to the `<ng-template>` element where it became a property binding,`[ngIf]`.
|
||||
* The rest of the `<div>`, including its class attribute, moved inside the `<ng-template>` element.
|
||||
|
||||
None of these forms are actually rendered.
|
||||
None of these forms are actually rendered.
|
||||
Only the finished product ends up in the DOM.
|
||||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"></img>
|
||||
<img src='content/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
Angular consumed the `<ng-template>` content during its actual rendering and
|
||||
Angular consumed the `<ng-template>` content during its actual rendering and
|
||||
replaced the `<ng-template>` with a diagnostic comment.
|
||||
|
||||
The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/structural-directives#ngSwitch) directives follow the same pattern.
|
||||
|
@ -279,7 +279,7 @@ The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/struc
|
|||
## Inside _*ngFor_
|
||||
|
||||
Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through
|
||||
template _attribute_ to `<ng-template>` _element_.
|
||||
template _attribute_ to `<ng-template>` _element_.
|
||||
|
||||
Here's a full-featured application of `NgFor`, written all three ways:
|
||||
|
||||
|
@ -294,15 +294,15 @@ This is manifestly more complicated than `ngIf` and rightly so.
|
|||
The `NgFor` directive has more features, both required and optional, than the `NgIf` shown in this guide.
|
||||
At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`).
|
||||
|
||||
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](guide/structural-directives#microsyntax).
|
||||
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](guide/structural-directives#microsyntax).
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
|
||||
Everything _outside_ the `ngFor` string stays with the host element
|
||||
(the `<div>`) as it moves inside the `<ng-template>`.
|
||||
Everything _outside_ the `ngFor` string stays with the host element
|
||||
(the `<div>`) as it moves inside the `<ng-template>`.
|
||||
In this example, the `[ngClass]="odd"` stays on the `<div>`.
|
||||
|
||||
|
||||
|
@ -318,12 +318,12 @@ In this example, the `[ngClass]="odd"` stays on the `<div>`.
|
|||
The Angular microsyntax lets you configure a directive in a compact, friendly string.
|
||||
The microsyntax parser translates that string into attributes on the `<ng-template>`:
|
||||
|
||||
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
|
||||
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
|
||||
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
|
||||
The parser translates `let hero`, `let i`, and `let odd` into variables named,
|
||||
The parser translates `let hero`, `let i`, and `let odd` into variables named,
|
||||
`let-hero`, `let-i`, and `let-odd`.
|
||||
|
||||
* The microsyntax parser takes `of` and `trackby`, title-cases them (`of` -> `Of`, `trackBy` -> `TrackBy`),
|
||||
* The microsyntax parser takes `of` and `trackby`, title-cases them (`of` -> `Of`, `trackBy` -> `TrackBy`),
|
||||
and prefixes them with the directive's attribute name (`ngFor`), yielding the names `ngForOf` and `ngForTrackBy`.
|
||||
Those are the names of two `NgFor` _input properties_ .
|
||||
That's how the directive learns that the list is `heroes` and the track-by function is `trackById`.
|
||||
|
@ -334,18 +334,18 @@ These properties include `index` and `odd` and a special property named `$implic
|
|||
* The `let-i` and `let-odd` variables were defined as `let i=index` and `let odd=odd`.
|
||||
Angular sets them to the current value of the context's `index` and `odd` properties.
|
||||
|
||||
* The context property for `let-hero` wasn't specified.
|
||||
It's intended source is implicit.
|
||||
* The context property for `let-hero` wasn't specified.
|
||||
It's intended source is implicit.
|
||||
Angular sets `let-hero` to the value of the context's `$implicit` property
|
||||
which `NgFor` has initialized with the hero for the current iteration.
|
||||
|
||||
* The [API guide](api/common/index/NgFor-directive "API: NgFor")
|
||||
* The [API guide](api/common/index/NgFor-directive "API: NgFor")
|
||||
describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
These microsyntax mechanisms are available to you when you write your own structural directives.
|
||||
Studying the
|
||||
Studying the
|
||||
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
|
||||
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
|
||||
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
|
||||
is a great way to learn more.
|
||||
|
||||
|
||||
|
@ -362,11 +362,11 @@ A _template input variable_ is a variable whose value you can reference _within_
|
|||
There are several such variables in this example: `hero`, `i`, and `odd`.
|
||||
All are preceded by the keyword `let`.
|
||||
|
||||
A _template input variable_ is **_not_** the same as a
|
||||
A _template input variable_ is **_not_** the same as a
|
||||
[template _reference_ variable](guide/template-syntax#ref-vars),
|
||||
neither _semantically_ nor _syntactically_.
|
||||
|
||||
You declare a template _input_ variable using the `let` keyword (`let hero`).
|
||||
You declare a template _input_ variable using the `let` keyword (`let hero`).
|
||||
The variable's scope is limited to a _single instance_ of the repeated template.
|
||||
You can use the same variable name again in the definition of other structural directives.
|
||||
|
||||
|
@ -385,10 +385,10 @@ variable as the `hero` declared as `#hero`.
|
|||
|
||||
Someday you'll want to repeat a block of HTML but only when a particular condition is true.
|
||||
You'll _try_ to put both an `*ngFor` and an `*ngIf` on the same host element.
|
||||
Angular won't let you. You may apply only one _structural_ directive to an element.
|
||||
Angular won't let you. You may apply only one _structural_ directive to an element.
|
||||
|
||||
The reason is simplicity. Structural directives can do complex things with the host element and its descendents.
|
||||
When two directives lay claim to the same host element, which one takes precedence?
|
||||
When two directives lay claim to the same host element, which one takes precedence?
|
||||
Which should go first, the `NgIf` or the `NgFor`? Can the `NgIf` cancel the effect of the `NgFor`?
|
||||
If so (and it seems like it should be so), how should Angular generalize the ability to cancel for other structural directives?
|
||||
|
||||
|
@ -417,12 +417,12 @@ Here's an example.
|
|||
The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
|
||||
(if any) of the switch cases are displayed.
|
||||
|
||||
`NgSwitch` itself is not a structural directive.
|
||||
`NgSwitch` itself is not a structural directive.
|
||||
It's an _attribute_ directive that controls the behavior of the other two switch directives.
|
||||
That's why you write `[ngSwitch]`, never `*ngSwitch`.
|
||||
|
||||
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
|
||||
You attach them to elements using the asterisk (*) prefix notation.
|
||||
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
|
||||
You attach them to elements using the asterisk (*) prefix notation.
|
||||
An `NgSwitchCase` displays its host element when its value matches the switch value.
|
||||
The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
|
||||
|
||||
|
@ -431,7 +431,7 @@ The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` m
|
|||
|
||||
|
||||
|
||||
The element to which you apply a directive is its _host_ element.
|
||||
The element to which you apply a directive is its _host_ element.
|
||||
The `<happy-hero>` is the host element for the happy `*ngSwitchCase`.
|
||||
The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
|
||||
|
||||
|
@ -440,7 +440,7 @@ The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
|
|||
|
||||
|
||||
|
||||
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
|
||||
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
|
||||
can be desugared into the template _attribute_ form.
|
||||
|
||||
|
||||
|
@ -480,7 +480,7 @@ You'll refer to the `<ng-template>` when you [write your own structural directiv
|
|||
## The *<ng-template>*
|
||||
|
||||
The <ng-template> is an Angular element for rendering HTML.
|
||||
It is never displayed directly.
|
||||
It is never displayed directly.
|
||||
In fact, before rendering the view, Angular _replaces_ the `<ng-template>` and its contents with a comment.
|
||||
|
||||
If there is no structural directive and you merely wrap some elements in a `<ng-template>`,
|
||||
|
@ -498,7 +498,7 @@ Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"></img>
|
||||
<img src='content/images/guide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -536,11 +536,11 @@ such as a `<div>`, and attach the directive to that wrapper.
|
|||
|
||||
|
||||
|
||||
Introducing another container element—typically a `<span>` or `<div>`—to
|
||||
group the elements under a single _root_ is usually harmless.
|
||||
Introducing another container element—typically a `<span>` or `<div>`—to
|
||||
group the elements under a single _root_ is usually harmless.
|
||||
_Usually_ ... but not _always_.
|
||||
|
||||
The grouping element may break the template appearance because CSS styles
|
||||
The grouping element may break the template appearance because CSS styles
|
||||
neither expect nor accommodate the new layout.
|
||||
For example, suppose you have the following paragraph layout.
|
||||
|
||||
|
@ -564,7 +564,7 @@ The constructed paragraph renders strangely.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"></img>
|
||||
<img src='content/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -572,7 +572,7 @@ The constructed paragraph renders strangely.
|
|||
The `p span` style, intended for use elsewhere, was inadvertently applied here.
|
||||
|
||||
Another problem: some HTML elements require all immediate children to be of a specific type.
|
||||
For example, the `<select>` element requires `<option>` children.
|
||||
For example, the `<select>` element requires `<option>` children.
|
||||
You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
|
||||
|
||||
When you try this,
|
||||
|
@ -588,7 +588,7 @@ the drop down is empty.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/bad-select.png' alt="spanned options don't work"></img>
|
||||
<img src='content/images/guide/structural-directives/bad-select.png' alt="spanned options don't work"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -598,7 +598,7 @@ The browser won't display an `<option>` within a `<span>`.
|
|||
### <ng-container> to the rescue
|
||||
|
||||
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout
|
||||
because Angular _doesn't put it in the DOM_.
|
||||
because Angular _doesn't put it in the DOM_.
|
||||
|
||||
Here's the conditional paragraph again, this time using `<ng-container>`.
|
||||
|
||||
|
@ -613,7 +613,7 @@ It renders properly.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"></img>
|
||||
<img src='content/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -631,13 +631,13 @@ The drop down works properly.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"></img>
|
||||
<img src='content/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
The `<ng-container>` is a syntax element recognized by the Angular parser.
|
||||
It's not a directive, component, class, or interface.
|
||||
It's not a directive, component, class, or interface.
|
||||
It's more like the curly braces in a JavaScript `if`-block:
|
||||
|
||||
|
||||
|
@ -646,7 +646,7 @@ It's more like the curly braces in a JavaScript `if`-block:
|
|||
statement1;
|
||||
statement2;
|
||||
statement3;
|
||||
}
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -709,9 +709,9 @@ Angular's own directives do not.
|
|||
|
||||
### _TemplateRef_ and _ViewContainerRef_
|
||||
|
||||
A simple structural directive like this one creates an
|
||||
A simple structural directive like this one creates an
|
||||
[_embedded view_](api/core/index/EmbeddedViewRef-class "API: EmbeddedViewRef")
|
||||
from the Angular-generated `<ng-template>` and inserts that view in a
|
||||
from the Angular-generated `<ng-template>` and inserts that view in a
|
||||
[_view container_](api/core/index/ViewContainerRef-class "API: ViewContainerRef")
|
||||
adjacent to the directive's original `<p>` host element.
|
||||
|
||||
|
@ -732,7 +732,7 @@ You inject both in the directive constructor as private variables of the class.
|
|||
### The _myUnless_ property
|
||||
|
||||
The directive consumer expects to bind a true/false condition to `[myUnless]`.
|
||||
That means the directive needs a `myUnless` property, decorated with `@Input`
|
||||
That means the directive needs a `myUnless` property, decorated with `@Input`
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
@ -758,7 +758,7 @@ Because the `myUnless` property does work, it needs a setter.
|
|||
* If the condition is falsy and the view hasn't been created previously,
|
||||
tell the _view container_ to create the _embedded view_ from the template.
|
||||
|
||||
* If the condition is truthy and the view is currently displayed,
|
||||
* If the condition is truthy and the view is currently displayed,
|
||||
clear the container which also destroys the view.
|
||||
|
||||
Nobody reads the `myUnless` property so it doesn't need a getter.
|
||||
|
@ -788,7 +788,7 @@ When the `condition` is truthy, the top (A) paragraph is removed and the bottom
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"></img>
|
||||
<img src='content/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -799,7 +799,7 @@ When the `condition` is truthy, the top (A) paragraph is removed and the bottom
|
|||
|
||||
## Summary
|
||||
|
||||
You can both try and download the source code for this guide in the <live-example></live-example>.
|
||||
You can both try and download the source code for this guide in the <live-example></live-example>.
|
||||
|
||||
Here is the source from the `src/app/` folder.
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@ Learn how to write templates that display data and consume user events with the
|
|||
|
||||
|
||||
|
||||
The Angular application manages what the user sees and can do, achieving this through the interaction of a
|
||||
The Angular application manages what the user sees and can do, achieving this through the interaction of a
|
||||
component class instance (the *component*) and its user-facing template.
|
||||
|
||||
You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM).
|
||||
You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM).
|
||||
In Angular, the component plays the part of the controller/viewmodel, and the template represents the view.
|
||||
|
||||
|
||||
|
@ -78,17 +78,17 @@ demonstrates all of the syntax and code snippets described in this guide.
|
|||
## HTML in templates
|
||||
|
||||
HTML is the language of the Angular template.
|
||||
Almost all HTML syntax is valid template syntax.
|
||||
The `<script>` element is a notable exception;
|
||||
Almost all HTML syntax is valid template syntax.
|
||||
The `<script>` element is a notable exception;
|
||||
it is forbidden, eliminating the risk of script injection attacks.
|
||||
In practice, `<script>` is ignored and a warning appears in the browser console.
|
||||
See the [Security](guide/security) page for details.
|
||||
|
||||
Some legal HTML doesn't make much sense in a template.
|
||||
The `<html>`, `<body>`, and `<base>` elements have no useful role.
|
||||
Some legal HTML doesn't make much sense in a template.
|
||||
The `<html>`, `<body>`, and `<base>` elements have no useful role.
|
||||
Pretty much everything else is fair game.
|
||||
|
||||
You can extend the HTML vocabulary of your templates with components and directives that appear as new elements and attributes.
|
||||
You can extend the HTML vocabulary of your templates with components and directives that appear as new elements and attributes.
|
||||
In the following sections, you'll learn how to get and set DOM (Document Object Model) values dynamically through data binding.
|
||||
|
||||
Begin with the first form of data binding—interpolation—to see how much richer template HTML can be.
|
||||
|
@ -1067,7 +1067,7 @@ content harmlessly.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/template-syntax/evil-title.png' alt="evil title made safe" width='500px'></img>
|
||||
<img src='content/images/guide/template-syntax/evil-title.png' alt="evil title made safe" width='500px'></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1837,7 +1837,7 @@ Here are all variations in action, including the uppercase version:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/template-syntax/ng-model-anim.gif' alt="NgModel variations"></img>
|
||||
<img src='content/images/guide/template-syntax/ng-model-anim.gif' alt="NgModel variations"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2132,7 +2132,7 @@ Here is an illustration of the _trackBy_ effect.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/template-syntax/ng-for-track-by-anim.gif' alt="trackBy"></img>
|
||||
<img src='content/images/guide/template-syntax/ng-for-track-by-anim.gif' alt="trackBy"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2167,7 +2167,7 @@ Angular puts only the *selected* element into the DOM.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/template-syntax/switch-anim.gif' alt="trackBy"></img>
|
||||
<img src='content/images/guide/template-syntax/switch-anim.gif' alt="trackBy"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2417,7 +2417,7 @@ The terms _input_ and _output_ reflect the perspective of the target directive.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/template-syntax/input-output.png' alt="Inputs and outputs"></img>
|
||||
<img src='content/images/guide/template-syntax/input-output.png' alt="Inputs and outputs"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2669,5 +2669,5 @@ It works perfectly with long property paths such as `a?.b?.c?.d`.
|
|||
|
||||
|
||||
## Summary
|
||||
You've completed this survey of template syntax.
|
||||
You've completed this survey of template syntax.
|
||||
Now it's time to put that knowledge to work on your own components and directives.
|
||||
|
|
|
@ -389,7 +389,7 @@ The Angular CLI has different commands to do the same thing. Adjust accordingly.
|
|||
After a few moments, karma opens a browser and starts writing to the console.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/testing/karma-browser.png' style="width:400px;" alt="Karma browser"></img>
|
||||
<img src='content/images/guide/testing/karma-browser.png' style="width:400px;" alt="Karma browser"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -473,7 +473,7 @@ Debug specs in the browser in the same way that you debug an application.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/testing/karma-1st-spec-debug.png' style="width:700px;" alt="Karma debugging"></img>
|
||||
<img src='content/images/guide/testing/karma-1st-spec-debug.png' style="width:700px;" alt="Karma debugging"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1914,7 +1914,7 @@ Inspect and download _all_ of the guide's application test code with this <live-
|
|||
The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action"></img>
|
||||
<img src='content/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2505,7 +2505,7 @@ A better solution is to create an artificial test component that demonstrates al
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/testing/highlight-directive-spec.png' width="200px" alt="HighlightDirective spec in action"></img>
|
||||
<img src='content/images/guide/testing/highlight-directive-spec.png' width="200px" alt="HighlightDirective spec in action"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2847,7 +2847,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
|||
|
||||
Simulates the passage of time and the completion of pending asynchronous activities
|
||||
by flushing both _timer_ and _micro-task_ queues within the _fakeAsync test zone_.
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -2855,7 +2855,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
|||
|
||||
The curious, dedicated reader might enjoy this lengthy blog post,
|
||||
"<a href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/"
|
||||
>_Tasks, microtasks, queues and schedules_</a>".
|
||||
>_Tasks, microtasks, queues and schedules_</a>".
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -3601,7 +3601,7 @@ Here are the most useful `DebugElement` members for testers, in approximate orde
|
|||
|
||||
|
||||
The immediate `DebugElement` children. Walk the tree by descending through `children`.
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -3609,7 +3609,7 @@ Here are the most useful `DebugElement` members for testers, in approximate orde
|
|||
|
||||
`DebugElement` also has `childNodes`, a list of `DebugNode` objects.
|
||||
`DebugElement` derives from `DebugNode` objects and there are often
|
||||
more nodes than elements. Testers can usually ignore plain nodes.
|
||||
more nodes than elements. Testers can usually ignore plain nodes.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ Incrementally upgrade an AngularJS application to Angular.
|
|||
_Angular_ is the name for the Angular of today and tomorrow.
|
||||
_AngularJS_ is the name for all v1.x versions of Angular.
|
||||
|
||||
AngularJS apps are great.
|
||||
AngularJS apps are great.
|
||||
Always consider the business case before moving to Angular.
|
||||
An important part of that case is the time and effort to get there.
|
||||
This guide describes the built-in tools for efficiently migrating AngularJS projects over to the
|
||||
|
@ -336,7 +336,7 @@ everything work seamlessly:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application" width="700"></img>
|
||||
<img src="content/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application" width="700"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -381,7 +381,7 @@ ways:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application" width="500"></img>
|
||||
<img src="content/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application" width="500"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -435,7 +435,7 @@ AngularJS and Angular approaches. Here's what happens:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application" width="600"></img>
|
||||
<img src="content/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application" width="600"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -473,7 +473,7 @@ that describes Angular assets in metadata. The differences blossom from there.
|
|||
|
||||
In a hybrid application we run both versions of Angular at the same time.
|
||||
That means that we need at least one module each from both AngularJS and Angular.
|
||||
We will import `UpgradeModule` inside our Angular module, and then use it for
|
||||
We will import `UpgradeModule` inside our Angular module, and then use it for
|
||||
bootstrapping our AngularJS module. Let's see how.
|
||||
|
||||
|
||||
|
@ -542,7 +542,7 @@ Angular from bootstrapping itself in the form of the `ngDoBootstrap` empty class
|
|||
|
||||
Now we bootstrap `AppModule` using `platformBrowserDynamic`'s `bootstrapModule` method.
|
||||
Then we use dependency injection to get a hold of the `UpgradeModule` instance in `AppModule`,
|
||||
and use it to bootstrap our AngularJS app.
|
||||
and use it to bootstrap our AngularJS app.
|
||||
The `upgrade.bootstrap` method takes the exact same arguments as [angular.bootstrap](https://docs.angularjs.org/api/ng/function/angular.bootstrap):
|
||||
|
||||
|
||||
|
@ -553,7 +553,7 @@ The `upgrade.bootstrap` method takes the exact same arguments as [angular.bootst
|
|||
|
||||
|
||||
We also need to install the `@angular/upgrade` package via `npm install @angular/upgrade --save`
|
||||
and add a mapping for the `@angular/upgrade/static` package:
|
||||
and add a mapping for the `@angular/upgrade/static` package:
|
||||
|
||||
|
||||
<code-example path="upgrade-module/src/systemjs.config.1.js" region="upgrade-static-umd" title="systemjs.config.js (map)">
|
||||
|
@ -569,7 +569,7 @@ existing AngularJS code works as before _and_ you're ready to run Angular code.
|
|||
### Using Angular Components from AngularJS Code
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/upgrade/ajs-to-a.png" alt="Using an Angular component from AngularJS code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/upgrade/ajs-to-a.png" alt="Using an Angular component from AngularJS code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -599,11 +599,11 @@ using the `downgradeComponent()` method. What we get when we do that is an Angul
|
|||
|
||||
|
||||
|
||||
Because `HeroDetailComponent` is an Angular component, we must also add it to the
|
||||
Because `HeroDetailComponent` is an Angular component, we must also add it to the
|
||||
`declarations` in the `AppModule`.
|
||||
|
||||
And because this component is being used from the AngularJS module, and is an entry point into
|
||||
our Angular application, we also need to add it to the `entryComponents` for our
|
||||
And because this component is being used from the AngularJS module, and is an entry point into
|
||||
our Angular application, we also need to add it to the `entryComponents` for our
|
||||
Angular module.
|
||||
|
||||
|
||||
|
@ -661,7 +661,7 @@ like this:
|
|||
|
||||
|
||||
These inputs and outputs can be supplied from the AngularJS template, and the
|
||||
`downgradeComponent()` method takes care of bridging them over via the `inputs`
|
||||
`downgradeComponent()` method takes care of bridging them over via the `inputs`
|
||||
and `outputs` arrays:
|
||||
|
||||
|
||||
|
@ -733,7 +733,7 @@ For example, we can easily make multiple copies of the component using `ng-repe
|
|||
### Using AngularJS Component Directives from Angular Code
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/upgrade/a-to-ajs.png" alt="Using an AngularJS component from Angular code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/upgrade/a-to-ajs.png" alt="Using an AngularJS component from Angular code" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -763,7 +763,7 @@ and a controller:
|
|||
|
||||
|
||||
|
||||
We can *upgrade* this component to Angular using the `UpgradeComponent` class.
|
||||
We can *upgrade* this component to Angular using the `UpgradeComponent` class.
|
||||
By creating a new Angular **directive** that extends `UpgradeComponent` and doing a `super` call
|
||||
inside it's constructor, we have a fully upgrade AngularJS component to be used inside Angular.
|
||||
All that is left is to add it to `AppModule`'s `declarations` array.
|
||||
|
@ -788,7 +788,7 @@ All that is left is to add it to `AppModule`'s `declarations` array.
|
|||
Upgraded components are Angular **directives**, instead of **components**, because Angular
|
||||
is unaware that AngularJS will create elements under it. As far as Angular knows, the upgraded
|
||||
component is just a directive - a tag - and Angular doesn't have to concern itself with
|
||||
it's children.
|
||||
it's children.
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -917,7 +917,7 @@ with one input and one output:
|
|||
|
||||
|
||||
|
||||
We can upgrade this component to Angular, annotate inputs and outputs in the upgrade directive,
|
||||
We can upgrade this component to Angular, annotate inputs and outputs in the upgrade directive,
|
||||
and then provide the input and output using Angular template syntax:
|
||||
|
||||
|
||||
|
@ -936,7 +936,7 @@ and then provide the input and output using Angular template syntax:
|
|||
### Projecting AngularJS Content into Angular Components
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/upgrade/ajs-to-a-with-projection.png" alt="Projecting AngularJS content into Angular" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/upgrade/ajs-to-a-with-projection.png" alt="Projecting AngularJS content into Angular" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -983,7 +983,7 @@ remains in "AngularJS land" and is managed by the AngularJS framework.
|
|||
### Transcluding Angular Content into AngularJS Component Directives
|
||||
|
||||
<figure>
|
||||
<img src="assets/images/guide/upgrade/a-to-ajs-with-transclusion.png" alt="Projecting Angular content into AngularJS" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
<img src="content/images/guide/upgrade/a-to-ajs-with-transclusion.png" alt="Projecting Angular content into AngularJS" align="left" style="width:250px; margin-left:-40px;margin-right:10px"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1045,14 +1045,14 @@ code. For example, we might have a service called `HeroesService` in AngularJS:
|
|||
|
||||
|
||||
We can upgrade the service using a Angular [Factory provider](guide/dependency-injection#factory-providers)
|
||||
that requests the service from the AngularJS `$injector`.
|
||||
that requests the service from the AngularJS `$injector`.
|
||||
|
||||
We recommend declaring the Factory Provider in a separate `ajs-upgraded-providers.ts` file
|
||||
so that they are all together, making it easier to reference them, create new ones and
|
||||
delete them once the upgrade is over.
|
||||
delete them once the upgrade is over.
|
||||
|
||||
It's also recommended to export the `heroesServiceFactory` function so that Ahead-of-Time
|
||||
compilation can pick it up.
|
||||
compilation can pick it up.
|
||||
|
||||
|
||||
<code-example path="upgrade-module/src/app/ajs-to-a-providers/ajs-upgraded-providers.ts" title="ajs-upgraded-providers.ts">
|
||||
|
@ -1116,8 +1116,8 @@ Again, as with Angular components, register the provider with the `NgModule` by
|
|||
|
||||
|
||||
|
||||
Now wrap the Angular `Heroes` in an *AngularJS factory function* using `downgradeInjectable()`.
|
||||
and plug the factory into an AngularJS module.
|
||||
Now wrap the Angular `Heroes` in an *AngularJS factory function* using `downgradeInjectable()`.
|
||||
and plug the factory into an AngularJS module.
|
||||
The name of the AngularJS dependency is up to you:
|
||||
|
||||
|
||||
|
@ -1140,15 +1140,15 @@ After this, the service is injectable anywhere in our AngularJS code:
|
|||
|
||||
We can take advantage of Ahead-of-time (AoT) compilation on hybrid apps just like on any other
|
||||
Angular application.
|
||||
The setup for an hybrid app is mostly the same as described in
|
||||
The setup for an hybrid app is mostly the same as described in
|
||||
[the Ahead-of-time Compilation chapter](guide/aot-compiler)
|
||||
save for differences in `index.html` and `main-aot.ts`
|
||||
|
||||
Our `index.html` will likely have script tags loading AngularJS files, so the `index.html` we
|
||||
use for AoT must also load those files.
|
||||
use for AoT must also load those files.
|
||||
An easy way to copy them is by adding each to the `copy-dist-files.js` file.
|
||||
|
||||
We also need to use `UpgradeModule` to bootstrap a hybrid app after bootstrapping the
|
||||
We also need to use `UpgradeModule` to bootstrap a hybrid app after bootstrapping the
|
||||
Module Factory:
|
||||
|
||||
|
||||
|
@ -1166,8 +1166,8 @@ And that's all we need to get the full benefit of AoT for Angular apps!
|
|||
|
||||
|
||||
The AoT metadata collector will not detect lifecycle hook methods on a parent class' prototype,
|
||||
so in order for upgraded components to work we needs to implement the lifecycle hooks
|
||||
on the upgraded component class and forward them to the `UpgradeComponent` parent.
|
||||
so in order for upgraded components to work we needs to implement the lifecycle hooks
|
||||
on the upgraded component class and forward them to the `UpgradeComponent` parent.
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -1182,9 +1182,9 @@ migrate all the routes in one fell swoop.
|
|||
But it would be much better to migrate routes one by one as they become upgraded.
|
||||
|
||||
The first step to have a dual router setup is to add an Angular root component containing
|
||||
one outlet for each router.
|
||||
one outlet for each router.
|
||||
AngularJS will use `ng-view`, and Angular will use `router-outlet`.
|
||||
When one is using it's router, the other outlet will be empty.
|
||||
When one is using it's router, the other outlet will be empty.
|
||||
|
||||
|
||||
<code-example path="upgrade-module/src/app/divide-routes/app.component.ts" title="app.component.ts">
|
||||
|
@ -1193,7 +1193,7 @@ When one is using it's router, the other outlet will be empty.
|
|||
|
||||
|
||||
|
||||
We want to use this component in the body of our `index.html` instead of an AngularJS component:
|
||||
We want to use this component in the body of our `index.html` instead of an AngularJS component:
|
||||
|
||||
|
||||
<code-example path="upgrade-module/src/index-divide-routes.html" region="body" title="app.component.ts (body)">
|
||||
|
@ -1219,9 +1219,9 @@ Next we declare both AngularJS and Angular routes as normal:
|
|||
|
||||
In our `app.module.ts` we need to add `AppComponent` to the declarations and boostrap array.
|
||||
|
||||
Next we configure the router itself.
|
||||
Next we configure the router itself.
|
||||
We want to use [hash navigation](guide/router#-hashlocationstrategy-) in Angular
|
||||
because that's what we're also using in AngularJS.
|
||||
because that's what we're also using in AngularJS.
|
||||
|
||||
Lastly, and most importantly, we want to use a custom `UrlHandlingStrategy` that will tell
|
||||
the Angular router which routes it should render - and only those.
|
||||
|
@ -1490,7 +1490,7 @@ Let's also add run scripts for the `tsc` TypeScript compiler to `package.json`:
|
|||
|
||||
|
||||
We can now install type definitions for the existing libraries that
|
||||
we're using but that don't come with prepackaged types: AngularJS and the
|
||||
we're using but that don't come with prepackaged types: AngularJS and the
|
||||
Jasmine unit test framework.
|
||||
|
||||
|
||||
|
@ -1666,7 +1666,7 @@ The project also contains some animations, which we are not yet upgrading in thi
|
|||
|
||||
|
||||
|
||||
Let's install Angular into the project, along with the SystemJS module loader.
|
||||
Let's install Angular into the project, along with the SystemJS module loader.
|
||||
Take a look at the results of the [Setup](guide/setup) instructions
|
||||
and get the following configurations from there:
|
||||
|
||||
|
@ -1716,14 +1716,14 @@ to load the actual application:
|
|||
|
||||
|
||||
|
||||
We also need to make a couple of adjustments
|
||||
to the `systemjs.config.js` file installed during [setup](guide/setup).
|
||||
|
||||
We also need to make a couple of adjustments
|
||||
to the `systemjs.config.js` file installed during [setup](guide/setup).
|
||||
|
||||
We want to point the browser to the project root when loading things through SystemJS,
|
||||
instead of using the `<base>` URL.
|
||||
|
||||
We also need to install the `upgrade` package via `npm install @angular/upgrade --save`
|
||||
and add a mapping for the `@angular/upgrade/static` package.
|
||||
and add a mapping for the `@angular/upgrade/static` package.
|
||||
|
||||
|
||||
<code-example path="upgrade-phonecat-2-hybrid/systemjs.config.1.js" region="paths" title="systemjs.config.js">
|
||||
|
@ -1735,7 +1735,7 @@ and add a mapping for the `@angular/upgrade/static` package.
|
|||
### Creating the _AppModule_
|
||||
|
||||
Now create the root `NgModule` class called `AppModule`.
|
||||
There is already a file named `app.module.ts` that holds the AngularJS module.
|
||||
There is already a file named `app.module.ts` that holds the AngularJS module.
|
||||
Rename it to `app.module.ajs.ts` and update the corresponding script name in the `index.html` as well.
|
||||
The file contents remain:
|
||||
|
||||
|
@ -1773,10 +1773,10 @@ we first need to import `UpgradeModule` in our `AppModule`, and override it's bo
|
|||
|
||||
Our application is currently bootstrapped using the AngularJS `ng-app` directive
|
||||
attached to the `<html>` element of the host page. This will no longer work with
|
||||
Angular. We should switch to a JavaScript-driven bootstrap instead.
|
||||
Angular. We should switch to a JavaScript-driven bootstrap instead.
|
||||
|
||||
So, remove the `ng-app` attribute from `index.html`, and instead bootstrap via `src/main.ts`.
|
||||
This file has been configured as the application entrypoint in `systemjs.config.js`,
|
||||
This file has been configured as the application entrypoint in `systemjs.config.js`,
|
||||
so it is already being loaded by the browser.
|
||||
|
||||
|
||||
|
@ -1802,7 +1802,7 @@ so let's do that next.
|
|||
|
||||
#### Why declare _angular_ as _angular.IAngularStatic_?
|
||||
|
||||
`@types/angular` is declared as a UMD module, and due to the way
|
||||
`@types/angular` is declared as a UMD module, and due to the way
|
||||
<a href="https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#support-for-umd-module-definitions">UMD typings</a>
|
||||
work, once you have an ES6 `import` statement in a file all UMD typed modules must also be
|
||||
imported via `import` statements instead of being globally available.
|
||||
|
@ -1810,11 +1810,11 @@ imported via `import` statements instead of being globally available.
|
|||
AngularJS is currently loaded by a script tag in `index.html`, which means that the whole app
|
||||
has access to it as a global and uses the same instance of the `angular` variable.
|
||||
If we used `import * as angular from 'angular'` instead we would also need to overhaul how we
|
||||
load every file in our AngularJS app to use ES6 modules in order to ensure AngularJS was being
|
||||
load every file in our AngularJS app to use ES6 modules in order to ensure AngularJS was being
|
||||
loaded correctly.
|
||||
|
||||
This is a considerable effort and it often isn't worth it, especially since we are in the
|
||||
process of moving our our to Angular already.
|
||||
This is a considerable effort and it often isn't worth it, especially since we are in the
|
||||
process of moving our our to Angular already.
|
||||
Instead we declare `angular` as `angular.IAngularStatic` to indicate it is a global variable
|
||||
and still have full typing support.
|
||||
|
||||
|
@ -1834,7 +1834,7 @@ ngResource and we're using it for two things:
|
|||
* For loading the details of a single phone into the phone detail component.
|
||||
|
||||
We can replace this implementation with an Angular service class, while
|
||||
keeping our controllers in AngularJS land.
|
||||
keeping our controllers in AngularJS land.
|
||||
|
||||
In the new version, we import the Angular HTTP module and call its `Http` service instead of `ngResource`.
|
||||
|
||||
|
@ -1908,7 +1908,7 @@ Notice that we're importing the `map` operator of the RxJS `Observable` separate
|
|||
We need to do this for all RxJS operators that we want to use, since Angular
|
||||
does not load all of them by default.
|
||||
|
||||
The new `Phone` service has the same features as the original, `ngResource`-based service.
|
||||
The new `Phone` service has the same features as the original, `ngResource`-based service.
|
||||
Because it's an Angular service, we register it with the `NgModule` providers:
|
||||
|
||||
|
||||
|
@ -1996,7 +1996,7 @@ with Angular's two-way `[(ngModel)]` binding syntax:
|
|||
|
||||
|
||||
|
||||
Replace the list's `ng-repeat` with an `*ngFor` as
|
||||
Replace the list's `ng-repeat` with an `*ngFor` as
|
||||
[described in the Template Syntax page](guide/template-syntax#directives).
|
||||
Replace the image tag's `ng-src` with a binding to the native `src` property.
|
||||
|
||||
|
@ -2009,10 +2009,10 @@ Replace the image tag's `ng-src` with a binding to the native `src` property.
|
|||
|
||||
#### No Angular _filter_ or _orderBy_ filters
|
||||
The built-in AngularJS `filter` and `orderBy` filters do not exist in Angular,
|
||||
so we need to do the filtering and sorting ourselves.
|
||||
so we need to do the filtering and sorting ourselves.
|
||||
|
||||
We replaced the `filter` and `orderBy` filters with bindings to the `getPhones()` controller method,
|
||||
which implements the filtering and ordering logic inside the component itself.
|
||||
which implements the filtering and ordering logic inside the component itself.
|
||||
|
||||
|
||||
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.component.ts" region="getphones" title="app/phone-list/phone-list.component.ts">
|
||||
|
@ -2021,8 +2021,8 @@ which implements the filtering and ordering logic inside the component itself.
|
|||
|
||||
|
||||
|
||||
Now we need to downgrade our Angular component so we can use it in AngularJS.
|
||||
Instead of registering a component, we register a `phoneList` *directive*,
|
||||
Now we need to downgrade our Angular component so we can use it in AngularJS.
|
||||
Instead of registering a component, we register a `phoneList` *directive*,
|
||||
a downgraded version of the Angular component.
|
||||
|
||||
The `as angular.IDirectiveFactory` cast tells the TypeScript compiler
|
||||
|
@ -2035,7 +2035,7 @@ that the return value of the `downgradeComponent` method is a directive factory.
|
|||
|
||||
|
||||
|
||||
The new `PhoneListComponent` uses the Angular `ngModel` directive, located in the `FormsModule`.
|
||||
The new `PhoneListComponent` uses the Angular `ngModel` directive, located in the `FormsModule`.
|
||||
Add the `FormsModule` to `NgModule` imports, declare the new `PhoneListComponent` and
|
||||
finally add it to `entryComponents` since we downgraded it:
|
||||
|
||||
|
@ -2057,8 +2057,8 @@ Now set the remaining `phone-detail.component.ts` as follows:
|
|||
|
||||
|
||||
|
||||
This is similar to the phone list component.
|
||||
The new wrinkle is the `RouteParams` type annotation that identifies the `routeParams` dependency.
|
||||
This is similar to the phone list component.
|
||||
The new wrinkle is the `RouteParams` type annotation that identifies the `routeParams` dependency.
|
||||
|
||||
The AngularJS injector has an AngularJS router dependency called `$routeParams`,
|
||||
which was injected into `PhoneDetails` when it was still an AngularJS controller.
|
||||
|
@ -2128,7 +2128,7 @@ Let's turn that into an Angular **pipe**.
|
|||
|
||||
There is no upgrade method to convert filters into pipes.
|
||||
You won't miss it.
|
||||
It's easy to turn the filter function into an equivalent Pipe class.
|
||||
It's easy to turn the filter function into an equivalent Pipe class.
|
||||
The implementation is the same as before, repackaged in the `transform` method.
|
||||
Rename the file to `checkmark.pipe.ts` to conform with Angular conventions:
|
||||
|
||||
|
@ -2154,7 +2154,7 @@ remove the filter <script> tag from `index.html`:
|
|||
To use AoT with our hybrid app we have to first set it up like any other Angular application,
|
||||
as shown in [the Ahead-of-time Compilation chapter](guide/aot-compiler).
|
||||
|
||||
Then we have to change `main-aot.ts` bootstrap also bootstrap the AngularJS app
|
||||
Then we have to change `main-aot.ts` bootstrap also bootstrap the AngularJS app
|
||||
via `UpgradeModule`:
|
||||
|
||||
|
||||
|
@ -2198,7 +2198,7 @@ their Angular counterparts, even though we're still serving them from the Angula
|
|||
Most AngularJS apps have more than a couple of routes though, and it's very helpful to migrate
|
||||
one route at a time.
|
||||
|
||||
Let's start by migrating the initial `/` and `/phones` routes to Angular,
|
||||
Let's start by migrating the initial `/` and `/phones` routes to Angular,
|
||||
while keeping `/phones/:phoneId` in the AngularJS router.
|
||||
|
||||
#### Add the Angular router
|
||||
|
@ -2207,7 +2207,7 @@ Angular has an [all-new router](guide/router).
|
|||
|
||||
Like all routers, it needs a place in the UI to display routed views.
|
||||
For Angular that's the `<router-outlet>` and it belongs in a *root component*
|
||||
at the top of the applications component tree.
|
||||
at the top of the applications component tree.
|
||||
|
||||
We don't yet have such a root component, because the app is still managed as an AngularJS app.
|
||||
Create a new `app.component.ts` file with the following `AppComponent` class:
|
||||
|
@ -2223,7 +2223,7 @@ It has a simple template that only includes the `<router-outlet>` for Angular ro
|
|||
and `ng-view` for AngularJS routes.
|
||||
This component just renders the contents of the active route and nothing else.
|
||||
|
||||
The selector tells Angular to plug this root component into the `<phonecat-app>`
|
||||
The selector tells Angular to plug this root component into the `<phonecat-app>`
|
||||
element on the host web page when the application launches.
|
||||
|
||||
Add this `<phonecat-app>` element to the `index.html`.
|
||||
|
@ -2239,7 +2239,7 @@ It replaces the old AngularJS `ng-view` directive:
|
|||
#### Create the _Routing Module_
|
||||
A router needs configuration whether it's the AngularJS or Angular or any other router.
|
||||
|
||||
The details of Angular router configuration are best left to the [Routing documentation](guide/router)
|
||||
The details of Angular router configuration are best left to the [Routing documentation](guide/router)
|
||||
which recommends that you create a `NgModule` dedicated to router configuration
|
||||
(called a _Routing Module_).
|
||||
|
||||
|
@ -2251,10 +2251,10 @@ which recommends that you create a `NgModule` dedicated to router configuration
|
|||
|
||||
|
||||
This module defines a `routes` object with one route to the phone list component
|
||||
and a default route for the empty path.
|
||||
and a default route for the empty path.
|
||||
It passes the `routes` to the `RouterModule.forRoot` method which does the rest.
|
||||
|
||||
A couple of extra providers enable routing with "hash" URLs such as `#!/phones`
|
||||
A couple of extra providers enable routing with "hash" URLs such as `#!/phones`
|
||||
instead of the default "push state" strategy.
|
||||
|
||||
There's a twist to our Routing Module though: we're also adding a custom `UrlHandlingStrategy`
|
||||
|
@ -2262,7 +2262,7 @@ that tells the Angular router to only process the `/` and `/phones` routes.
|
|||
|
||||
Now update the `AppModule` to import this `AppRoutingModule` and also the
|
||||
declare the root `AppComponent` as the bootstrap component.
|
||||
That tells Angular that it should bootstrap the app with the _root_ `AppComponent` and
|
||||
That tells Angular that it should bootstrap the app with the _root_ `AppComponent` and
|
||||
insert it's view into the host web page.
|
||||
|
||||
We can also remove the `ngDoBootstrap()` override from `app.module.ts` since we are now
|
||||
|
@ -2289,7 +2289,7 @@ Now we need to tell the AngularJS router to only process the `/phones/:phoneId`
|
|||
|
||||
#### Generate links for each phone
|
||||
|
||||
We no longer have to hardcode the links to phone details in the phone list.
|
||||
We no longer have to hardcode the links to phone details in the phone list.
|
||||
We can generate data bindings for each phone's `id` to the `routerLink` directive
|
||||
and let that directive construct the appropriate URL to the `PhoneDetailComponent`:
|
||||
|
||||
|
@ -2332,11 +2332,11 @@ Extract the `phoneId` from the `ActivatedRoute.snapshot.params` and fetch the ph
|
|||
|
||||
|
||||
|
||||
Since this was the last route we want to migrate over, we can also now delete the last
|
||||
Since this was the last route we want to migrate over, we can also now delete the last
|
||||
route config from `app/app.config.ts`, and add it to the Angular router configuration.
|
||||
|
||||
We don't need our `UrlHandlingStrategy` anymore either, since now Angular is processing all
|
||||
routes.
|
||||
routes.
|
||||
|
||||
|
||||
<code-example path="upgrade-phonecat-4-final/app/app-routing.module.ts" title="app/app-routing.module.ts">
|
||||
|
@ -2356,8 +2356,8 @@ do with removing code - which of course is every programmer's favorite task!
|
|||
The application is still bootstrapped as a hybrid app.
|
||||
There's no need for that anymore.
|
||||
|
||||
Switch the bootstrap method of the application from the `UpgradeAdapter`
|
||||
to the Angular way.
|
||||
Switch the bootstrap method of the application from the `UpgradeAdapter`
|
||||
to the Angular way.
|
||||
|
||||
|
||||
<code-example path="upgrade-phonecat-4-final/app/main.ts" title="main.ts">
|
||||
|
@ -2366,11 +2366,11 @@ to the Angular way.
|
|||
|
||||
|
||||
|
||||
If you haven't already, remove all references to the `UpgradeModule` from `app.module.ts`,
|
||||
as well as any [Factory provider](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
||||
If you haven't already, remove all references to the `UpgradeModule` from `app.module.ts`,
|
||||
as well as any [Factory provider](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
||||
for AngularJS services, and the `app/ajs-upgraded-providers.ts` file.
|
||||
|
||||
Also remove any `downgradeInjectable()` or `downgradeComponent()` you find,
|
||||
Also remove any `downgradeInjectable()` or `downgradeComponent()` you find,
|
||||
together with the associated AngularJS factory or directive declarations.
|
||||
Since we have no downgraded components anymore, we also don't need to have them listed
|
||||
in `entryComponents` either.
|
||||
|
@ -2453,8 +2453,8 @@ The following change is needed in `protractor-conf.js` to sync with hybrid apps:
|
|||
|
||||
|
||||
|
||||
The next set of changes is when we start to upgrade components and their template to Angular.
|
||||
This is because the E2E tests have matchers that are specific to AngularJS.
|
||||
The next set of changes is when we start to upgrade components and their template to Angular.
|
||||
This is because the E2E tests have matchers that are specific to AngularJS.
|
||||
For PhoneCat we need to make the following changes in order to make things work with Angular:
|
||||
|
||||
|
||||
|
@ -2596,7 +2596,7 @@ When the bootstrap method is switched from that of `UpgradeModule` to
|
|||
pure Angular, AngularJS ceases to exist on the page completely.
|
||||
At this point we need to tell Protractor that it should not be looking for
|
||||
an AngularJS app anymore, but instead it should find *Angular apps* from
|
||||
the page.
|
||||
the page.
|
||||
|
||||
Replace the `ng12Hybrid` previously added with the following in `protractor-conf.js`:
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ In this case, `target` refers to the [`<input>` element](https://developer.mozil
|
|||
After each call, the `onKey()` method appends the contents of the input box value to the list
|
||||
in the component's `values` property, followed by a separator character (|).
|
||||
The [interpolation](guide/template-syntax#interpolation)
|
||||
displays the accumulating input box changes from the `values` property.
|
||||
displays the accumulating input box changes from the `values` property.
|
||||
|
||||
Suppose the user enters the letters "abc", and then backspaces to remove them one by one.
|
||||
Here's what the UI displays:
|
||||
|
@ -98,7 +98,7 @@ Here's what the UI displays:
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/user-input/keyup1-anim.gif' alt="key up 1"></img>
|
||||
<img src='content/images/guide/user-input/keyup1-anim.gif' alt="key up 1"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -127,7 +127,7 @@ for `event.target.value` in which case the same user input would produce:
|
|||
### Type the _$event_
|
||||
|
||||
The example above casts the `$event` as an `any` type.
|
||||
That simplifies the code at a cost.
|
||||
That simplifies the code at a cost.
|
||||
There is no type information
|
||||
that could reveal properties of the event object and prevent silly mistakes.
|
||||
|
||||
|
@ -139,7 +139,7 @@ The following example rewrites the method with types:
|
|||
|
||||
|
||||
|
||||
The `$event` is now a specific `KeyboardEvent`.
|
||||
The `$event` is now a specific `KeyboardEvent`.
|
||||
Not all elements have a `value` property so it casts `target` to an input element.
|
||||
The `OnKey` method more clearly expresses what it expects from the template and how it interprets the event.
|
||||
|
||||
|
@ -147,7 +147,7 @@ The `OnKey` method more clearly expresses what it expects from the template and
|
|||
Typing the event object reveals a significant objection to passing the entire DOM event into the method:
|
||||
the component has too much awareness of the template details.
|
||||
It can't extract information without knowing more than it should about the HTML implementation.
|
||||
That breaks the separation of concerns between the template (_what the user sees_)
|
||||
That breaks the separation of concerns between the template (_what the user sees_)
|
||||
and the component (_how the application processes user data_).
|
||||
|
||||
The next section shows how to use template reference variables to address this problem.
|
||||
|
@ -181,7 +181,7 @@ Type something in the input box, and watch the display update with each keystrok
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back"></img>
|
||||
<img src='content/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -195,8 +195,8 @@ Type something in the input box, and watch the display update with each keystrok
|
|||
Angular updates the bindings (and therefore the screen)
|
||||
only if the app does something in response to asynchronous events, such as keystrokes.
|
||||
This example code binds the `keyup` event
|
||||
to the number 0, the shortest template statement possible.
|
||||
While the statement does nothing useful,
|
||||
to the number 0, the shortest template statement possible.
|
||||
While the statement does nothing useful,
|
||||
it satisfies Angular's requirement so that Angular will update the screen.
|
||||
|
||||
</div>
|
||||
|
@ -223,7 +223,7 @@ The `(keyup)` event handler hears *every keystroke*.
|
|||
Sometimes only the _Enter_ key matters, because it signals that the user has finished typing.
|
||||
One way to reduce the noise would be to examine every `$event.keyCode` and take action only when the key is _Enter_.
|
||||
|
||||
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_.
|
||||
|
||||
<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" title="src/app/keyup.components.ts (v3)" linenums="false">
|
||||
|
@ -235,7 +235,7 @@ Then Angular calls the event handler only when the user presses _Enter_.
|
|||
Here's how it works.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/user-input/keyup3-anim.gif' alt="key up 3"></img>
|
||||
<img src='content/images/guide/user-input/keyup3-anim.gif' alt="key up 3"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -269,7 +269,7 @@ clicking **Add**.
|
|||
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"></img>
|
||||
<img src='content/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"></img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -336,4 +336,4 @@ quickly become verbose and clumsy when handling large amounts of user input.
|
|||
Two-way data binding is a more elegant and compact way to move
|
||||
values between data entry fields and model properties.
|
||||
The next page, `Forms`, explains how to write
|
||||
two-way bindings with `NgModel`.
|
||||
two-way bindings with `NgModel`.
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 320 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 488 KiB After Width: | Height: | Size: 488 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 851 KiB After Width: | Height: | Size: 851 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |