chore(aio): blank line before each list-item; - becomes * (#3481)
This commit is contained in:
parent
5738c4b66a
commit
a56c4a0b08
|
@ -288,16 +288,6 @@ include ../../../_includes/_util-fns
|
|||
|
||||
+makeJson('styleguide/package.1.json', {paths: 'dependencies'}, "package.json dependencies", { pnk: [/(\S*zone.*)/, /(\Score-js.*)/, /(\Ssystem.*)/ ]})
|
||||
|
||||
:marked
|
||||
As well as styling across multiple lines.
|
||||
|
||||
code-example(format="" language="js").
|
||||
- var styles = { pnk: /(^.*dependencies[\s\S]* \})/gm };
|
||||
+makeJson('styleguide/package.1.json', {paths: 'name, version, dependencies '}, "Foo", styles )
|
||||
|
||||
- var styles = { pnk: /(^.*dependencies[\s\S]* \})/gm };
|
||||
+makeJson('styleguide/package.1.json', {paths: 'name, version, dependencies '}, "Foo", styles )
|
||||
|
||||
:marked
|
||||
### Inline code and code examples provided directly i.e. not from an example file.
|
||||
|
||||
|
@ -307,10 +297,10 @@ include ../../../_includes/_util-fns
|
|||
This style has several named attributes
|
||||
|
||||
#### code-example attributes
|
||||
- *name:* Name displayed in Tab (required for tabs)
|
||||
- *language:* javascript, html, etc.
|
||||
- *escape:* html (escapes html, woot!)
|
||||
- *format:* linenums (or linenums:4 specify starting line)
|
||||
* *name:* Name displayed in Tab (required for tabs)
|
||||
* *language:* javascript, html, etc.
|
||||
* *escape:* html (escapes html, woot!)
|
||||
* *format:* linenums (or linenums:4 specify starting line)
|
||||
|
||||
#### Example
|
||||
|
||||
|
|
|
@ -17,17 +17,17 @@ include ../_util-fns
|
|||
|
||||
We will build a simple form from scratch, one step at a time. Along the way we'll learn
|
||||
|
||||
- How to build an Angular form with a component and template
|
||||
* How to build an Angular form with a component and template
|
||||
|
||||
- The `ngModel` two-way data binding syntax for reading and writing values to input controls
|
||||
* The `ngModel` two-way data binding syntax for reading and writing values to input controls
|
||||
|
||||
- The `ngModel` directive in combination with a form to track the change state and validity of form controls
|
||||
* The `ngModel` directive in combination with a form to track the change state and validity of form controls
|
||||
|
||||
- The Special CSS classes that follow the state of the controls and can be used to provide strong visual feedback
|
||||
* The Special CSS classes that follow the state of the controls and can be used to provide strong visual feedback
|
||||
|
||||
- How to display validation errors to users and enable/disable form controls
|
||||
* How to display validation errors to users and enable/disable form controls
|
||||
|
||||
- How to share information across controls with template local variables
|
||||
* How to share information across controls with template local variables
|
||||
|
||||
<live-example>Live Example</live-example>
|
||||
|
||||
|
@ -148,18 +148,19 @@ code-example(format="").
|
|||
|
||||
Understanding this component requires only the Angular concepts we’ve learned in previous chapters
|
||||
|
||||
1. We use the `ng.core` object from the Angular library as we usually do.
|
||||
* We use the `ng.core` object from the Angular library as we usually do.
|
||||
|
||||
1. The `Component()` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
|
||||
* The `Component()` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
|
||||
|
||||
1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`.
|
||||
* The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`.
|
||||
|
||||
* We defined dummy data for `model` and `powers` as befits a demo.
|
||||
|
||||
1. We defined dummy data for `model` and `powers` as befits a demo.
|
||||
Down the road, we can inject a data service to get and save real data
|
||||
or perhaps expose these properties as [inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
|
||||
parent component. None of this concerns us now and these future changes won't affect our form.
|
||||
|
||||
1. We threw in a `diagnostic` method at the end to return a JSON representation of our model.
|
||||
* We threw in a `diagnostic` method at the end to return a JSON representation of our model.
|
||||
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
|
||||
|
||||
Why don't we write the template inline in the component file as we often do
|
||||
|
|
|
@ -663,8 +663,9 @@ table(width="100%")
|
|||
+makeExample('cb-ajs-quick-reference/ts/src/app/app.module.1.ts')(format=".")
|
||||
:marked
|
||||
Angular modules, defined with the `NgModule` decorator, serve the same purpose:
|
||||
- `imports`: specifies the list of other modules that this module depends upon
|
||||
- `declaration`: keeps track of your components, pipes, and directives.
|
||||
|
||||
* `imports`: specifies the list of other modules that this module depends upon
|
||||
* `declaration`: keeps track of your components, pipes, and directives.
|
||||
|
||||
For more information on modules, see [Angular Modules (NgModule)](../guide/ngmodule.html).
|
||||
tr(style=top)
|
||||
|
|
|
@ -8,25 +8,30 @@ a#toc
|
|||
:marked
|
||||
# Contents
|
||||
|
||||
- [Overview](overview)
|
||||
- [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit)
|
||||
- [Why do AOT compilation?](#why-aot)
|
||||
- [Compile with AOT](#compile)
|
||||
- [Bootstrap](#bootstrap)
|
||||
- [Tree shaking](#tree-shaking)
|
||||
- [Rollup](#rollup)
|
||||
- [Rollup Plugins](#rollup-plugins)
|
||||
- [Run Rollup](#run-rollup)
|
||||
- [Load the bundle](#load)
|
||||
- [Serve the app](#serve)
|
||||
- [AOT QuickStart source code](#source-code)
|
||||
- [Workflow and convenience script](#workflow)
|
||||
- [Develop JIT along with AOT](#run-jit)
|
||||
- [Tour of Heroes](#toh)
|
||||
- [JIT in development, AOT in production](#jit-dev-aot-prod)
|
||||
- [Tree shaking](#shaking)
|
||||
- [Running the application](#running-app)
|
||||
- [Inspect the Bundle](#inspect-bundle)
|
||||
* [Overview](overview)
|
||||
* [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit)
|
||||
* [Why do AOT compilation?](#why-aot)
|
||||
* [Compile with AOT](#compile)
|
||||
* [Bootstrap](#bootstrap)
|
||||
* [Tree shaking](#tree-shaking)
|
||||
|
||||
*[Rollup](#rollup)
|
||||
*[Rollup Plugins](#rollup-plugins)
|
||||
*[Run Rollup](#run-rollup)
|
||||
|
||||
* [Load the bundle](#load)
|
||||
* [Serve the app](#serve)
|
||||
* [AOT QuickStart source code](#source-code)
|
||||
* [Workflow and convenience script](#workflow)
|
||||
|
||||
*[Develop JIT along with AOT](#run-jit)
|
||||
|
||||
* [Tour of Heroes](#toh)
|
||||
|
||||
*[JIT in development, AOT in production](#jit-dev-aot-prod)
|
||||
*[Tree shaking](#shaking)
|
||||
*[Running the application](#running-app)
|
||||
*[Inspect the Bundle](#inspect-bundle)
|
||||
|
||||
|
||||
a#overview
|
||||
|
|
|
@ -15,11 +15,11 @@ include ../_util-fns
|
|||
:marked
|
||||
# Contents
|
||||
|
||||
- [Pass data from parent to child with input binding](#parent-to-child)
|
||||
- [Intercept input property changes with a setter](#parent-to-child-setter)
|
||||
- [Intercept input property changes with `ngOnChanges()`](#parent-to-child-on-changes)
|
||||
- [Parent calls an `@ViewChild()`](#parent-to-view-child)
|
||||
- [Parent and children communicate via a service](#bidirectional-service)
|
||||
* [Pass data from parent to child with input binding](#parent-to-child)
|
||||
* [Intercept input property changes with a setter](#parent-to-child-setter)
|
||||
* [Intercept input property changes with `ngOnChanges()`](#parent-to-child-on-changes)
|
||||
* [Parent calls an `@ViewChild()`](#parent-to-view-child)
|
||||
* [Parent and children communicate via a service](#bidirectional-service)
|
||||
|
||||
|
||||
:marked
|
||||
|
|
|
@ -337,7 +337,6 @@ figure.image-display
|
|||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
figure.image-display
|
||||
img(src="/resources/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios")
|
||||
:marked
|
||||
|
||||
<a id="providers"></a>
|
||||
.l-main-section
|
||||
|
@ -847,7 +846,6 @@ a(id="provideparent")
|
|||
:marked
|
||||
And here's how we could use it with a different parent type:
|
||||
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','beth-providers')(format='.')
|
||||
:marked
|
||||
|
||||
a(id="forwardref")
|
||||
.l-main-section
|
||||
|
|
|
@ -17,17 +17,24 @@ a#toc
|
|||
|
||||
* [Simple template-driven forms](#template1)
|
||||
* [Template-driven forms with validation messages in code](#template2)
|
||||
- [Component Class](#component-class)
|
||||
- [The benefits of messages in code](#improvement)
|
||||
- [`FormModule` and template-driven forms](#formmodule)
|
||||
|
||||
* [Component Class](#component-class)
|
||||
* [The benefits of messages in code](#improvement)
|
||||
* [`FormModule` and template-driven forms](#formmodule)
|
||||
|
||||
* [Reactive forms with validation in code](#reactive)
|
||||
- [Switch to the `ReactiveFormsModule`](#reactive-forms-module)
|
||||
- [Component template](#reactive-component-template)
|
||||
- [Component class](#reactive-component-class)
|
||||
- [`FormBuilder` declaration](#formbuilder)
|
||||
- [Committing hero value changes](#committing-changes)
|
||||
|
||||
* [Switch to the `ReactiveFormsModule`](#reactive-forms-module)
|
||||
* [Component template](#reactive-component-template)
|
||||
* [Component class](#reactive-component-class)
|
||||
|
||||
* [`FormBuilder` declaration](#formbuilder)
|
||||
* [Committing hero value changes](#committing-changes)
|
||||
|
||||
* [Custom validation](#custom-validation)
|
||||
- [Custom validation directive](#custom-validation-directive)
|
||||
|
||||
* [Custom validation directive](#custom-validation-directive)
|
||||
|
||||
* [Testing considerations](#testing)
|
||||
|
||||
a#live-example
|
||||
|
@ -60,22 +67,23 @@ a#template1
|
|||
|
||||
:marked
|
||||
Note the following:
|
||||
- The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
|
||||
|
||||
- The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
|
||||
* The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
|
||||
|
||||
* The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
|
||||
with an Angular form control called `name` in its internal control model.
|
||||
|
||||
- The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
|
||||
* The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
|
||||
|
||||
- The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
|
||||
* The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
|
||||
This gives you a reference to the Angular `NgModel` directive
|
||||
associated with this control that you can use _in the template_
|
||||
to check for control states such as `valid` and `dirty`.
|
||||
|
||||
- The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
|
||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
|
||||
the control is either `dirty` or `touched`.
|
||||
|
||||
- Each nested `<div>` can present a custom message for one of the possible validation errors.
|
||||
* Each nested `<div>` can present a custom message for one of the possible validation errors.
|
||||
There are messages for `required`, `minlength`, and `maxlength`.
|
||||
|
||||
The full template repeats this kind of layout for each data entry control on the form.
|
||||
|
@ -136,16 +144,17 @@ a#template2
|
|||
|
||||
:marked
|
||||
The `<input>` element HTML is almost the same. There are noteworthy differences:
|
||||
- The hard-code error message `<divs>` are gone.
|
||||
|
||||
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
|
||||
* The hard-code error message `<divs>` are gone.
|
||||
|
||||
* There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
|
||||
It invalidates the control if the user enters "bob" in the name `<input>`([try it](#live-example)).
|
||||
See the [custom validation](#custom-validation) section later in this cookbook for more information
|
||||
on custom validation directives.
|
||||
|
||||
- The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
|
||||
* The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
|
||||
|
||||
- Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
|
||||
* Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
|
||||
|
||||
a#component-class
|
||||
:marked
|
||||
|
@ -169,17 +178,17 @@ a#component-class
|
|||
:marked
|
||||
Some observations:
|
||||
|
||||
- Angular `@ViewChild` queries for a template variable when you pass it
|
||||
* Angular `@ViewChild` queries for a template variable when you pass it
|
||||
the name of that variable as a string (`'heroForm'` in this case).
|
||||
|
||||
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
|
||||
* The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
|
||||
Periodically inspecting it reveals these changes.
|
||||
|
||||
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
|
||||
* Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
|
||||
when anything changes in the view.
|
||||
That's the right time to see if there's a new `heroForm` object.
|
||||
|
||||
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
|
||||
* When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
|
||||
The `onValueChanged` handler looks for validation errors after every keystroke.
|
||||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.')
|
||||
|
||||
|
@ -193,9 +202,9 @@ a#component-class
|
|||
The messages are empty strings when the hero data are valid.
|
||||
|
||||
For each field, the `onValueChanged` handler does the following:
|
||||
- Clears the prior error message, if any.
|
||||
- Acquires the field's corresponding Angular form control.
|
||||
- If such a control exists _and_ it's been changed ("dirty")
|
||||
* Clears the prior error message, if any.
|
||||
* Acquires the field's corresponding Angular form control.
|
||||
* If such a control exists _and_ it's been changed ("dirty")
|
||||
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
|
||||
|
||||
Next, the component needs some error messages of course—a set for each validated property with
|
||||
|
@ -266,6 +275,7 @@ a#reactive
|
|||
This approach requires a bit more effort. *You have to write the control model and manage it*.
|
||||
|
||||
This allows you to do the following:
|
||||
|
||||
* Add, change, and remove validation functions on the fly.
|
||||
* Manipulate the control model dynamically from within the component.
|
||||
* [Test](#testing) validation and control logic with isolated unit tests.
|
||||
|
@ -303,10 +313,10 @@ a#reactive-component-template
|
|||
|
||||
:marked
|
||||
Key changes are:
|
||||
- The validation attributes are gone (except `required`) because
|
||||
* The validation attributes are gone (except `required`) because
|
||||
validating happens in code.
|
||||
|
||||
- `required` remains, not for validation purposes (that's in the code),
|
||||
* `required` remains, not for validation purposes (that's in the code),
|
||||
but rather for css styling and accessibility.
|
||||
|
||||
.l-sub-section
|
||||
|
@ -318,10 +328,10 @@ a#reactive-component-template
|
|||
to the control model, as you'll see below.
|
||||
|
||||
:marked
|
||||
- The `formControlName` replaces the `name` attribute; it serves the same
|
||||
* The `formControlName` replaces the `name` attribute; it serves the same
|
||||
purpose of correlating the input with the Angular form control.
|
||||
|
||||
- The two-way `[(ngModel)]` binding is gone.
|
||||
* The two-way `[(ngModel)]` binding is gone.
|
||||
The reactive approach does not use data binding to move data into and out of the form controls.
|
||||
That's all in code.
|
||||
|
||||
|
@ -346,15 +356,15 @@ a#reactive-component-class
|
|||
`reactive/hero-form-reactive.component.ts (FormBuilder),
|
||||
template/hero-form-template2.component.ts (ViewChild)`)
|
||||
:marked
|
||||
- Inject `FormBuilder` in a constructor.
|
||||
* Inject `FormBuilder` in a constructor.
|
||||
|
||||
- Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
|
||||
* Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
|
||||
because that's when you'll have the hero data. Call it again in the `addHero` method.
|
||||
.l-sub-section
|
||||
:marked
|
||||
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
|
||||
:marked
|
||||
- The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
|
||||
* The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
|
||||
Then it attaches the same `onValueChanged` handler (there's a one line difference)
|
||||
to the form's `valueChanges` event and calls it immediately
|
||||
to set error messages for the new control model.
|
||||
|
@ -387,6 +397,7 @@ a#committing-changes
|
|||
The developer decides _when and how_ to update the data model from control values.
|
||||
|
||||
This sample updates the model twice:
|
||||
|
||||
1. When the user submits the form.
|
||||
1. When the user adds a new hero.
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ a#top
|
|||
* [Create a translation source file with the **_ng-xi18n_ extraction tool**](#ng-xi18n)
|
||||
* [Translate text messages](#translate)
|
||||
* [Merge the completed translation file into the app](#merge)
|
||||
|
||||
* [Merge with the JIT compiler](#jit)
|
||||
* [Internationalization with the AOT compiler](#aot)
|
||||
|
||||
* [Translation file maintenance and _id_ changes](#maintenance)
|
||||
|
||||
:marked
|
||||
|
@ -164,6 +166,7 @@ a#cardinality
|
|||
categories and their matching values.
|
||||
|
||||
Pluralization categories include:
|
||||
|
||||
* =0
|
||||
* =1
|
||||
* =5
|
||||
|
@ -172,6 +175,7 @@ a#cardinality
|
|||
|
||||
Put the default _English_ translation in braces (`{}`) next to the pluralization category.
|
||||
* When you're talking about one wolf, you could write `=1 {one wolf}`.
|
||||
|
||||
* For zero wolves, you could write `=0 {no wolves}`.
|
||||
* For two wolves, you could write `=2 {two wolves}`.
|
||||
|
||||
|
@ -421,6 +425,7 @@ a#merge
|
|||
in another format (`.xlif` and `.xtb`) that Angular understands.
|
||||
|
||||
You provide the Angular compiler with three new pieces of information:
|
||||
|
||||
* the translation file
|
||||
* the translation file format
|
||||
* the <a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">_Locale ID_</a>
|
||||
|
@ -477,6 +482,7 @@ a#text-plugin
|
|||
creates those providers based on the user's _locale_
|
||||
and the corresponding translation file:
|
||||
+makeExample('cb-i18n/ts/src/app/i18n-providers.ts', null, 'src/app/i18n-providers.ts')
|
||||
|
||||
:marked
|
||||
1. It gets the locale from the global `document.locale` variable that was set in `index.html`.
|
||||
|
||||
|
@ -541,6 +547,7 @@ a#aot
|
|||
The result is a separate version of the application for each language.
|
||||
|
||||
Tell AOT how to translate by adding three options to the `ngc` command:
|
||||
|
||||
* `--i18nFile`: the path to the translation file
|
||||
* `--locale`: the name of the locale
|
||||
* `--i18nFormat`: the format of the localization file
|
||||
|
|
|
@ -95,6 +95,7 @@ a#q-what-not-to-declare
|
|||
Add only [declarable](#q-declarable) classes to a module's `declarations` list.
|
||||
|
||||
Do *not* declare the following:
|
||||
|
||||
* A class that's already declared in another module, whether an app module, @NgModule, or third-party module.
|
||||
* An array of directives imported from another module.
|
||||
For example, don't declare FORMS_DIRECTIVES from `@angular/forms`.
|
||||
|
@ -560,7 +561,6 @@ a#q-is-it-loaded
|
|||
Certain NgModules (such as `BrowserModule`) implement such a guard,
|
||||
such as this `CoreModule` constructor from the NgModules page.
|
||||
+makeExample('ngmodule/ts/src/app/core/core.module.ts', 'ctor', 'src/app/core/core.module.ts (Constructor)')(format='.')
|
||||
:marked
|
||||
|
||||
.l-hr
|
||||
|
||||
|
@ -597,6 +597,7 @@ a#q-entry-component-defined
|
|||
You must tell it about them by adding them to the `entryComponents` list.
|
||||
|
||||
Angular automatically adds the following types of components to the module's `entryComponents`:
|
||||
|
||||
* The component in the `@NgModule.bootstrap` list.
|
||||
* Components referenced in router configuration.
|
||||
|
||||
|
@ -723,6 +724,7 @@ a#q-module-recommendations
|
|||
Create feature modules around specific application business domains, user workflows, and utility collections.
|
||||
|
||||
Feature modules tend to fall into one of the following groups:
|
||||
|
||||
* [Domain feature modules](#domain-feature-module).
|
||||
* [Routed feature modules](#routed-feature-module).
|
||||
* [Routing modules](#routing-module).
|
||||
|
@ -803,6 +805,7 @@ table
|
|||
A routing module separates routing concerns from its companion module.
|
||||
|
||||
A routing module typically does the following:
|
||||
|
||||
* Defines routes.
|
||||
* Adds router configuration to the module's `imports`.
|
||||
* Re-exports `RouterModule`.
|
||||
|
@ -1159,6 +1162,7 @@ table
|
|||
The `RouterModule` adds routed components to that list.
|
||||
|
||||
That leaves only the following sources of undiscoverable components:
|
||||
|
||||
* Components bootstrapped using one of the imperative techniques.
|
||||
* Components dynamically loaded into the DOM by some means other than the router.
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ a#from-ts
|
|||
The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features.
|
||||
|
||||
The downgrade progression is
|
||||
|
||||
* _TypeScript_ to _ES6-with-decorators_
|
||||
* _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_")
|
||||
* _ES6-without-decorators_ to _ES5_
|
||||
|
@ -488,6 +489,7 @@ a#io-decorators
|
|||
|
||||
In _TypeScript_ and _ES6-with-decorators_,
|
||||
you precede the constructor parameters with injection qualifiers such as:
|
||||
|
||||
* [`@Optional`](../api/core/index/Optional-decorator.html) sets the parameter to `null` if the service is missing
|
||||
* [`@Attribute`](../api/core/index/Attribute-interface.html) to inject a host element attribute value
|
||||
* [`@ContentChild`](../api/core/index/ContentChild-decorator.html) to inject a content child
|
||||
|
|
|
@ -27,15 +27,15 @@ include ../_util-fns
|
|||
:marked
|
||||
The steps are as follows:
|
||||
|
||||
- [Prerequisite](#prereq1): Install Node.js
|
||||
- [Prerequisite](#prereq2): Install Visual Studio 2015 Update 3
|
||||
- [Prerequisite](#prereq3): Configure External Web tools
|
||||
- [Prerequisite](#prereq4): Install TypeScript 2 for Visual Studio 2015
|
||||
- [Step 1](#download): Download the QuickStart files
|
||||
- [Step 2](#create-project): Create the Visual Studio ASP.NET project
|
||||
- [Step 3](#copy): Copy the QuickStart files into the ASP.NET project folder
|
||||
- [Step 4](#restore): Restore required packages
|
||||
- [Step 5](#build-and-run): Build and run the app
|
||||
* [Prerequisite](#prereq1): Install Node.js
|
||||
* [Prerequisite](#prereq2): Install Visual Studio 2015 Update 3
|
||||
* [Prerequisite](#prereq3): Configure External Web tools
|
||||
* [Prerequisite](#prereq4): Install TypeScript 2 for Visual Studio 2015
|
||||
* [Step 1](#download): Download the QuickStart files
|
||||
* [Step 2](#create-project): Create the Visual Studio ASP.NET project
|
||||
* [Step 3](#copy): Copy the QuickStart files into the ASP.NET project folder
|
||||
* [Step 4](#restore): Restore required packages
|
||||
* [Step 5](#build-and-run): Build and run the app
|
||||
|
||||
.l-main-section
|
||||
h2#prereq1 Prerequisite: Node.js
|
||||
|
@ -81,6 +81,7 @@ h2#prereq4 Prerequisite: Install TypeScript 2 for Visual Studio 2015
|
|||
which you need to develop Angular applications.
|
||||
|
||||
To install TypeScript 2:
|
||||
|
||||
* Download and install **[TypeScript 2.0 for Visual Studio 2015](http://download.microsoft.com/download/6/D/8/6D8381B0-03C1-4BD2-AE65-30FF0A4C62DA/TS2.0.3-TS-release20-nightly-20160921.1/TypeScript_Dev14Full.exe)**
|
||||
* OR install it with npm: `npm install -g typescript@2.0`.
|
||||
|
||||
|
@ -121,6 +122,7 @@ h2#copy Step 3: Copy the QuickStart files into the ASP.NET project folder
|
|||
* Click the `Show All Files` button in Solution Explorer to reveal all of the hidden files in the project.
|
||||
* Right-click on each folder/file to be included in the project and select `Include in Project`.
|
||||
Minimally, include the following folder/files:
|
||||
|
||||
* app folder (answer *No* if asked to search for TypeScript Typings)
|
||||
* styles.css
|
||||
* index.html
|
||||
|
|
|
@ -181,6 +181,7 @@ a#component
|
|||
operations and supporting declaration syntax.
|
||||
|
||||
Read about the following forms of binding in the [Template Syntax](./template-syntax.html) page:
|
||||
|
||||
* [Interpolation](./template-syntax.html#interpolation).
|
||||
* [Property binding](./template-syntax.html#property-binding).
|
||||
* [Event binding](./template-syntax.html#event-binding).
|
||||
|
@ -417,6 +418,7 @@ a#jit
|
|||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||
|
||||
Angular calls these hook methods in the following order:
|
||||
|
||||
* `ngOnChanges`: when an [input](#input)/[output](#output) binding value changes.
|
||||
* `ngOnInit`: after the first `ngOnChanges`.
|
||||
* `ngDoCheck`: developer's custom change detection.
|
||||
|
@ -436,9 +438,10 @@ a#jit
|
|||
.alert.is-important
|
||||
:marked
|
||||
Angular has the following types of modules:
|
||||
- [Angular modules](#angular-module).
|
||||
|
||||
* [Angular modules](#angular-module).
|
||||
For details and examples, see the [Angular Modules](./ngmodule.html) page.
|
||||
- ES2015 modules, as described in this section.
|
||||
* ES2015 modules, as described in this section.
|
||||
|
||||
:marked
|
||||
A cohesive block of code dedicated to a single purpose.
|
||||
|
@ -538,10 +541,11 @@ a#Q
|
|||
The alternative technique is [template-driven forms](#template-driven-forms).
|
||||
|
||||
When building reactive forms:
|
||||
- The "source of truth" is the component. The validation is defined using code in the component.
|
||||
- Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
|
||||
- The template input elements do *not* use `ngModel`.
|
||||
- The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
|
||||
|
||||
* The "source of truth" is the component. The validation is defined using code in the component.
|
||||
* Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
|
||||
* The template input elements do *not* use `ngModel`.
|
||||
* The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
|
||||
|
||||
Reactive forms are powerful, flexible, and a good choice for more complex data-entry form scenarios, such as dynamic generation of form controls.
|
||||
|
||||
|
@ -658,10 +662,11 @@ a#structural-directives
|
|||
The alternate technique is [Reactive Forms](#reactive-forms).
|
||||
|
||||
When building template-driven forms:
|
||||
- The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
|
||||
- [Two-way binding](#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
|
||||
- Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
|
||||
- The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
|
||||
|
||||
* The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
|
||||
* [Two-way binding](#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
|
||||
* Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
|
||||
* The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
|
||||
|
||||
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data-entry form scenarios.
|
||||
|
||||
|
|
|
@ -249,15 +249,15 @@ figure
|
|||
Here are a few of the most useful `@Component` configuration options:
|
||||
|
||||
:marked
|
||||
- `selector`: CSS selector that tells Angular to create and insert an instance of this component
|
||||
* `selector`: CSS selector that tells Angular to create and insert an instance of this component
|
||||
where it finds a `<hero-list>` tag in *parent* HTML.
|
||||
For example, if an app's HTML contains `<hero-list></hero-list>`, then
|
||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
- `templateUrl`: module-relative address of this component's HTML template, shown [above](#templates).
|
||||
* `templateUrl`: module-relative address of this component's HTML template, shown [above](#templates).
|
||||
|
||||
:marked
|
||||
- `providers`: array of **dependency injection providers** for services that the component requires.
|
||||
* `providers`: array of **dependency injection providers** for services that the component requires.
|
||||
This is one way to tell Angular that the component's constructor requires a `HeroService`
|
||||
so it can get the list of heroes to display.
|
||||
|
||||
|
@ -404,6 +404,7 @@ figure
|
|||
<br class="l-clear-both">
|
||||
:marked
|
||||
Examples include:
|
||||
|
||||
* logging service
|
||||
* data service
|
||||
* message bus
|
||||
|
|
|
@ -358,10 +358,10 @@ figure.image-display
|
|||
|
||||
This page covered how to:
|
||||
|
||||
- [Build an **attribute directive**](#write-directive) that modifies the behavior of an element.
|
||||
- [Apply the directive](#apply-directive) to an element in a template.
|
||||
- [Respond to **events**](#respond-to-user) that change the directive's behavior.
|
||||
- [**Bind** values to the directive](#bindings).
|
||||
* [Build an **attribute directive**](#write-directive) that modifies the behavior of an element.
|
||||
* [Apply the directive](#apply-directive) to an element in a template.
|
||||
* [Respond to **events**](#respond-to-user) that change the directive's behavior.
|
||||
* [**Bind** values to the directive](#bindings).
|
||||
|
||||
The final source code follows:
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ block includes
|
|||
the QuickStart repo update instructions</a>.
|
||||
|
||||
Notably:
|
||||
|
||||
* `app/main.ts` moved to `src/main.ts`.
|
||||
* `app/` moved to `src/app/`.
|
||||
* `index.html`, `styles.css` and `tsconfig.json` moved inside `src/`.
|
||||
|
@ -52,15 +53,18 @@ block includes
|
|||
choosing the approach that best fits each scenario.
|
||||
|
||||
## NEW: Deployment guide (2017-01-30)
|
||||
|
||||
The new [Deployment](deployment.html) guide describes techniques for putting your application on a server.
|
||||
It includes important advice on optimizing for production.
|
||||
|
||||
## Hierarchical Dependency Injection: refreshed (2017-01-13)
|
||||
|
||||
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) guide is significantly revised.
|
||||
Closes issue #3086.
|
||||
Revised samples are clearer and cover all topics discussed.
|
||||
|
||||
## Miscellaneous (2017-01-05)
|
||||
|
||||
* [Setup](setup.html) guide:
|
||||
added (optional) instructions on how to remove _non-essential_ files.
|
||||
* No longer consolidate RxJS operator imports in `rxjs-extensions` file; each file should import what it needs.
|
||||
|
@ -68,28 +72,34 @@ block includes
|
|||
* [Style Guide](style-guide.html): copy edits and revised rules.
|
||||
|
||||
## Router: more detail (2016-12-21)
|
||||
|
||||
Added more information to the [Router](router.html) guide
|
||||
including sections named outlets, wildcard routes, and preload strategies.
|
||||
|
||||
## HTTP: how to set default request headers (and other request options) (2016-12-14)
|
||||
|
||||
Added section on how to set default request headers (and other request options) to
|
||||
[HTTP](server-communication.html#override-default-request-options) guide.
|
||||
|
||||
## Testing: added component test plunkers (2016-12-02)
|
||||
|
||||
Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: <live-example name="setup" plnkr="quickstart-specs">one</live-example> for the QuickStart seed's `AppComponent` and <live-example name="testing" plnkr="banner-specs">another</live-example> for the Testing guide's `BannerComponent`.
|
||||
Linked to these plunkers in [Testing](testing.html#live-examples) and [Setup anatomy](setup-systemjs-anatomy) guides.
|
||||
|
||||
## Internationalization: pluralization and _select_ (2016-11-30)
|
||||
|
||||
The [Internationalization (i18n)](../cookbook/i18n.html) guide explains how to handle pluralization and
|
||||
translation of alternative texts with `select`.
|
||||
The sample demonstrates these features too.
|
||||
|
||||
## Testing: karma file updates (2016-11-30)
|
||||
|
||||
* `karma.config` + `karma-test-shim` can handle multiple spec source paths;
|
||||
see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294).
|
||||
* Displays Jasmine Runner output in the karma-launched browser.
|
||||
|
||||
## QuickStart Rewrite (2016-11-18)
|
||||
|
||||
The QuickStart is completely rewritten so that it actually is quick.
|
||||
It references a minimal "Hello Angular" app running in Plunker.
|
||||
The new [Setup](setup.html) page tells you how to install a local development environment
|
||||
|
@ -97,9 +107,11 @@ block includes
|
|||
You are no longer asked to copy-and-paste code into setup files that were not explained anyway.
|
||||
|
||||
## Sync with Angular v.2.2.0 (2016-11-14)
|
||||
|
||||
Docs and code samples updated and tested with Angular v.2.2.0.
|
||||
|
||||
## UPDATE: NgUpgrade Guide for the AOT friendly _upgrade/static_ module (2016-11-14)
|
||||
|
||||
The updated [NgUpgrade Guide](upgrade.html) guide covers the
|
||||
new AOT friendly `upgrade/static` module
|
||||
released in v.2.2.0, which is the recommended
|
||||
|
@ -107,15 +119,18 @@ block includes
|
|||
The documentation for the version prior to v.2.2.0 has been removed.
|
||||
|
||||
## ES6 described in "TypeScript to JavaScript" (2016-11-14)
|
||||
|
||||
The updated [TypeScript to JavaScript](../cookbook/ts-to-js.html) cookbook
|
||||
now explains how to write apps in ES6/7
|
||||
by translating the common idioms in the TypeScript documentation examples
|
||||
(and elsewhere on the web) to ES6/7 and ES5.
|
||||
|
||||
## Sync with Angular v.2.1.1 (2016-10-21)
|
||||
|
||||
Docs and code samples updated and tested with Angular v.2.1.1.
|
||||
|
||||
## npm _@types_ packages replace _typings_ (2016-10-20)
|
||||
|
||||
Documentation samples now get TypeScript type information for 3rd party libraries
|
||||
from npm `@types` packages rather than with the _typings_ tooling.
|
||||
The `typings.json` file is gone.
|
||||
|
@ -125,34 +140,41 @@ block includes
|
|||
packages in support of upgrade; these are not needed for pure Angular development.
|
||||
|
||||
## "Template Syntax" explains two-way data binding syntax (2016-10-20)
|
||||
|
||||
Demonstrates how to two-way data bind to a custom Angular component and
|
||||
re-explains `[(ngModel)]` in terms of the basic `[()]` syntax.
|
||||
|
||||
## BREAKING CHANGE: `in-memory-web-api` (v.0.1.11) delivered as esm umd (2016-10-19)
|
||||
|
||||
This change supports ES6 developers and aligns better with typical Angular libraries.
|
||||
It does not affect the module's API but it does affect how you load and import it.
|
||||
See the <a href="https://github.com/angular/in-memory-web-api/blob/master/CHANGELOG.md#0113-2016-10-20" target="_blank">change note</a>
|
||||
in the `in-memory-web-api` repo.
|
||||
|
||||
## "Router" _preload_ syntax and _:enter_/_:leave_ animations (2016-10-19)
|
||||
|
||||
The router can lazily _preload_ modules _after_ the app starts and
|
||||
_before_ the user navigates to them for improved perceived performance.
|
||||
|
||||
New `:enter` and `:leave` aliases make animation more natural.
|
||||
|
||||
## Sync with Angular v.2.1.0 (2016-10-12)
|
||||
|
||||
Docs and code samples updated and tested with Angular v.2.1.0.
|
||||
|
||||
## NEW "Ahead of time (AOT) Compilation" cookbook (2016-10-11)
|
||||
|
||||
The NEW [Ahead of time (AOT) Compilation](../cookbook/aot-compiler.html) cookbook
|
||||
explains what AOT compilation is and why you'd want it.
|
||||
It demonstrates the basics with a QuickStart app
|
||||
followed by the more advanced considerations of compiling and bundling the Tour of Heroes.
|
||||
|
||||
## Sync with Angular v.2.0.2 (2016-10-6)
|
||||
|
||||
Docs and code samples updated and tested with Angular v.2.0.2.
|
||||
|
||||
## "Routing and Navigation" guide with the _Router Module_ (2016-10-5)
|
||||
|
||||
The [Routing and Navigation](router.html) guide now locates route configuration
|
||||
in a _Routing Module_.
|
||||
The _Routing Module_ replaces the previous _routing object_ involving the `ModuleWithProviders`.
|
||||
|
|
|
@ -126,6 +126,7 @@ a(id='loading-styles')
|
|||
## Loading styles into components
|
||||
|
||||
There are several ways to add styles to a component:
|
||||
|
||||
* By setting `styles` or `styleUrls` metadata.
|
||||
* Inline in the template HTML.
|
||||
* With CSS imports.
|
||||
|
@ -215,9 +216,11 @@ a#view-encapsulation
|
|||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Appendix 1](#inspect-generated-css).
|
||||
|
||||
* `None` means that Angular does no view encapsulation.
|
||||
Angular adds the CSS to the global styles.
|
||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
||||
|
@ -256,6 +259,7 @@ code-example(format="").
|
|||
|
||||
:marked
|
||||
There are two kinds of generated attributes:
|
||||
|
||||
* An element that would be a shadow DOM host in native encapsulation has a
|
||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
||||
* An element within a component's view has a `_ngcontent` attribute
|
||||
|
|
|
@ -12,32 +12,38 @@ block includes
|
|||
:marked
|
||||
# Contents
|
||||
|
||||
- [Why dependency injection?](#why-di)
|
||||
- [Angular dependency injection](#angular-dependency-injection)
|
||||
- [Configuring the injector](#injector-config)
|
||||
- [Registering providers in an `NgModule`](#register-providers-ngmodule)
|
||||
- [Registering providers in a component](#register-providers-component)
|
||||
- [When to use `NgModule` versus an application component](#ngmodule-vs-comp)
|
||||
- [Preparing the `HeroListComponent` for injection](#prep-for-injection)
|
||||
- [Implicit injector creation](#di-metadata)
|
||||
- [Singleton services](#singleton-services)
|
||||
- [Testing the component](#testing-the-component)
|
||||
- [When the service needs a service](#service-needs-service)
|
||||
- [Why `@Injectable()`?](#injectable)
|
||||
- [Creating and registering a logger service](#logger-service)
|
||||
- [Injector providers](#injector-providers)
|
||||
- [The `Provider` class and `provide` object literal](#provide)
|
||||
- [Alternative class providers](#class-provider)
|
||||
- [Class provider with dependencies](#class-provider-dependencies)
|
||||
- [Aliased class providers](#aliased-class-providers)
|
||||
- [Value providers](#value-provider)
|
||||
- [Factory providers](#factory-provider)
|
||||
- [Dependency injection tokens](#dependency-injection-tokens)
|
||||
- [Non-class dependencies](#non-class-dependencies)
|
||||
- [`OpaqueToken`](#opaquetoken)
|
||||
- [Optional dependencies](#optional)
|
||||
- [Summary](#summary)
|
||||
- [Appendix: Working with injectors directly](#explicit-injector)
|
||||
* [Why dependency injection?](#why-di)
|
||||
* [Angular dependency injection](#angular-dependency-injection)
|
||||
|
||||
* [Configuring the injector](#injector-config)
|
||||
* [Registering providers in an `NgModule`](#register-providers-ngmodule)
|
||||
* [Registering providers in a component](#register-providers-component)
|
||||
* [When to use `NgModule` versus an application component](#ngmodule-vs-comp)
|
||||
* [Preparing the `HeroListComponent` for injection](#prep-for-injection)
|
||||
* [Implicit injector creation](#di-metadata)
|
||||
* [Singleton services](#singleton-services)
|
||||
* [Testing the component](#testing-the-component)
|
||||
* [When the service needs a service](#service-needs-service)
|
||||
* [Why `@Injectable()`?](#injectable)
|
||||
|
||||
* [Creating and registering a logger service](#logger-service)
|
||||
* [Injector providers](#injector-providers)
|
||||
|
||||
* [The `Provider` class and `provide` object literal](#provide)
|
||||
* [Alternative class providers](#class-provider)
|
||||
* [Class provider with dependencies](#class-provider-dependencies)
|
||||
* [Aliased class providers](#aliased-class-providers)
|
||||
* [Value providers](#value-provider)
|
||||
* [Factory providers](#factory-provider)
|
||||
|
||||
* [Dependency injection tokens](#dependency-injection-tokens)
|
||||
|
||||
* [Non-class dependencies](#non-class-dependencies)
|
||||
* [`OpaqueToken`](#opaquetoken)
|
||||
|
||||
* [Optional dependencies](#optional)
|
||||
* [Summary](#summary)
|
||||
* [Appendix: Working with injectors directly](#explicit-injector)
|
||||
|
||||
Run the <live-example></live-example>.
|
||||
|
||||
|
@ -493,6 +499,7 @@ a#injectable
|
|||
## Creating and registering a logger service
|
||||
|
||||
Inject a logger into `HeroService` in two steps:
|
||||
|
||||
1. Create the logger service.
|
||||
1. Register it with the application.
|
||||
|
||||
|
|
|
@ -196,14 +196,14 @@ a#optimize
|
|||
|
||||
If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses.
|
||||
|
||||
- Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
|
||||
- Bundling: concatenates modules into a single file (bundle).
|
||||
- Inlining: pulls template html and css into the components.
|
||||
- Minification: removes excess whitespace, comments, and optional tokens.
|
||||
- Uglification: rewrites code to use short, cryptic variable and function names.
|
||||
- Dead code elimination: removes unreferenced modules and unused code.
|
||||
- Pruned libraries: drop unused libraries and pare others down to the features you need.
|
||||
- Performance measurement: focus on optimizations that make a measurable difference.
|
||||
* Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
|
||||
* Bundling: concatenates modules into a single file (bundle).
|
||||
* Inlining: pulls template html and css into the components.
|
||||
* Minification: removes excess whitespace, comments, and optional tokens.
|
||||
* Uglification: rewrites code to use short, cryptic variable and function names.
|
||||
* Dead code elimination: removes unreferenced modules and unused code.
|
||||
* Pruned libraries: drop unused libraries and pare others down to the features you need.
|
||||
* Performance measurement: focus on optimizations that make a measurable difference.
|
||||
|
||||
Each tool does something different.
|
||||
They work best in combination and are mutually reinforcing.
|
||||
|
@ -220,6 +220,7 @@ a#aot
|
|||
during the build process.
|
||||
|
||||
Apps compiled with AOT launch faster for several reasons.
|
||||
|
||||
* Application components execute immediately, without client-side compilation.
|
||||
* Templates are embedded as code within their components so there is no client-side request for template files.
|
||||
* You don't download the Angular compiler, which is pretty big on its own.
|
||||
|
@ -414,10 +415,10 @@ a#deep-link
|
|||
|
||||
#### Development servers
|
||||
|
||||
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
|
||||
* [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
|
||||
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
|
||||
|
||||
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
|
||||
* [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
|
||||
`historyApiFallback` entry in the dev server options as follows:
|
||||
|
||||
code-example().
|
||||
|
@ -429,10 +430,11 @@ code-example().
|
|||
:marked
|
||||
#### Production servers
|
||||
|
||||
- [Apache](https://httpd.apache.org/): add a
|
||||
* [Apache](https://httpd.apache.org/): add a
|
||||
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
|
||||
to the `.htaccess` file as show
|
||||
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
|
||||
|
||||
code-example(format=".").
|
||||
RewriteEngine On
|
||||
# If an existing asset or directory is requested go to it as it is
|
||||
|
@ -444,7 +446,7 @@ code-example(format=".").
|
|||
RewriteRule ^ /index.html
|
||||
|
||||
:marked
|
||||
- [NGinx](http://nginx.org/): use `try_files`, as described in
|
||||
* [NGinx](http://nginx.org/): use `try_files`, as described in
|
||||
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
||||
modified to serve `index.html`:
|
||||
|
||||
|
@ -452,7 +454,7 @@ code-example(format=".").
|
|||
try_files $uri $uri/ /index.html;
|
||||
|
||||
:marked
|
||||
- [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
[here](http://stackoverflow.com/a/26152011/2116927):
|
||||
code-example(format="." escape="html").
|
||||
<system.webServer>
|
||||
|
@ -471,7 +473,7 @@ code-example(format="." escape="html").
|
|||
</system.webServer>
|
||||
|
||||
:marked
|
||||
- [GitHub Pages](https://pages.github.com/): you can't
|
||||
* [GitHub Pages](https://pages.github.com/): you can't
|
||||
[directly configure](https://github.com/isaacs/github/issues/408)
|
||||
the GitHub Pages server, but you can add a 404 page.
|
||||
Copy `index.html` into `404.html`.
|
||||
|
@ -481,7 +483,7 @@ code-example(format="." escape="html").
|
|||
and to
|
||||
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
|
||||
|
||||
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
||||
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
||||
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
|
||||
|
||||
code-example(format=".").
|
||||
|
|
|
@ -188,6 +188,7 @@ figure.image-display
|
|||
|
||||
:marked
|
||||
That brief syntax does a lot:
|
||||
|
||||
* Declares a constructor parameter and its type.
|
||||
* Declares a public property of the same name.
|
||||
* Initializes that property with the corresponding argument when creating an instance of the class.
|
||||
|
@ -252,10 +253,11 @@ figure.image-display
|
|||
:marked
|
||||
## Summary
|
||||
Now you know how to use:
|
||||
- **Interpolation** with double curly braces to display a component property.
|
||||
- **ngFor** to display an array of items.
|
||||
- A TypeScript class to shape the **model data** for your component and display properties of that model.
|
||||
- **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
|
||||
|
||||
* **Interpolation** with double curly braces to display a component property.
|
||||
* **ngFor** to display an array of items.
|
||||
* A TypeScript class to shape the **model data** for your component and display properties of that model.
|
||||
* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
|
||||
|
||||
Here's the final code:
|
||||
|
||||
|
|
|
@ -14,12 +14,12 @@ include ../_util-fns
|
|||
|
||||
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
||||
|
||||
- Build an Angular form with a component and template.
|
||||
- Use `ngModel` to create two-way data bindings for reading and writing input-control values.
|
||||
- Track state changes and the validity of form controls.
|
||||
- Provide visual feedback using special CSS classes that track the state of the controls.
|
||||
- Display validation errors to users and enable/disable form controls.
|
||||
- Share information across HTML elements using template reference variables.
|
||||
* Build an Angular form with a component and template.
|
||||
* Use `ngModel` to create two-way data bindings for reading and writing input-control values.
|
||||
* Track state changes and the validity of form controls.
|
||||
* Provide visual feedback using special CSS classes that track the state of the controls.
|
||||
* Display validation errors to users and enable/disable form controls.
|
||||
* Share information across HTML elements using template reference variables.
|
||||
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
|
@ -128,16 +128,18 @@ figure.image-display
|
|||
|
||||
Understanding this component requires only the Angular concepts covered in previous pages.
|
||||
|
||||
- The code imports the Angular core library and the `Hero` model you just created.
|
||||
- The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
|
||||
- The `templateUrl` property points to a separate file for the template HTML.
|
||||
- You defined dummy data for `model` and `powers`, as befits a demo.
|
||||
* The code imports the Angular core library and the `Hero` model you just created.
|
||||
* The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
|
||||
* The `templateUrl` property points to a separate file for the template HTML.
|
||||
* You defined dummy data for `model` and `powers`, as befits a demo.
|
||||
|
||||
Down the road, you can inject a data service to get and save real data
|
||||
or perhaps expose these properties as inputs and outputs
|
||||
(see [Input and output properties](./template-syntax.html#inputs-outputs) on the
|
||||
[Template Syntax](./template-syntax.html) page) for binding to a
|
||||
parent component. This is not a concern now and these future changes won't affect the form.
|
||||
- You added a `diagnostic` property to return a JSON representation of the model.
|
||||
|
||||
* You added a `diagnostic` property to return a JSON representation of the model.
|
||||
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
||||
|
||||
### Why the separate template file?
|
||||
|
@ -333,9 +335,9 @@ figure.image-display
|
|||
|
||||
.l-sub-section
|
||||
:marked
|
||||
- Each input element has an `id` property that is used by the `label` element's `for` attribute
|
||||
* Each input element has an `id` property that is used by the `label` element's `for` attribute
|
||||
to match the label to its input control.
|
||||
- Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
||||
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
||||
|
||||
:marked
|
||||
If you run the app now and change every hero model property, the form might display like this:
|
||||
|
@ -445,8 +447,9 @@ figure.image-display
|
|||
|
||||
:marked
|
||||
To achieve this effect, extend the `<input>` tag with the following:
|
||||
- A [template reference variable](./template-syntax.html#ref-vars).
|
||||
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
|
||||
|
||||
* A [template reference variable](./template-syntax.html#ref-vars).
|
||||
* The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
|
||||
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
|
||||
|
@ -639,15 +642,15 @@ figure.image-display
|
|||
The Angular form discussed in this page takes advantage of the following
|
||||
framework features to provide support for data modification, validation, and more:
|
||||
|
||||
- An Angular HTML form template.
|
||||
- A form component class with a `@Component` decorator.
|
||||
- Handling form submission by binding to the `NgForm.ngSubmit` event property.
|
||||
- Template-reference variables such as `#heroForm` and `#name`.
|
||||
- `[(ngModel)]` syntax for two-way data binding.
|
||||
- The use of `name` attributes for validation and form-element change tracking.
|
||||
- The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
||||
- Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
|
||||
- Custom CSS classes that provide visual feedback to users about invalid controls.
|
||||
* An Angular HTML form template.
|
||||
* A form component class with a `@Component` decorator.
|
||||
* Handling form submission by binding to the `NgForm.ngSubmit` event property.
|
||||
* Template-reference variables such as `#heroForm` and `#name`.
|
||||
* `[(ngModel)]` syntax for two-way data binding.
|
||||
* The use of `name` attributes for validation and form-element change tracking.
|
||||
* The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
||||
* Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
|
||||
* Custom CSS classes that provide visual feedback to users about invalid controls.
|
||||
|
||||
The final project folder structure should look like this:
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ figure
|
|||
|
||||
:marked
|
||||
## Contents
|
||||
|
||||
* [Component lifecycle hooks overview](#hooks-overview)
|
||||
* [Lifecycle sequence](#hooks-purpose-timing)
|
||||
* [Interfaces are optional (technically)](#interface-optional)
|
||||
|
@ -25,13 +26,18 @@ figure
|
|||
* [Lifecycle examples](#the-sample)
|
||||
* [Peek-a-boo: all hooks](#peek-a-boo)
|
||||
* [Spying OnInit and OnDestroy](#spy)
|
||||
|
||||
* [OnInit](#oninit)
|
||||
* [OnDestroy](#ondestroy)
|
||||
|
||||
* [OnChanges](#onchanges)
|
||||
* [DoCheck](#docheck)
|
||||
* [AfterView](#afterview)
|
||||
|
||||
* [Abide by the unidirectional data flow rule](#wait-a-tick)
|
||||
|
||||
* [AfterContent](#aftercontent)
|
||||
|
||||
* [Content projection](#content-projection)
|
||||
* [AfterContent hooks](#aftercontent-hooks)
|
||||
* [No unidirectional flow worries with _AfterContent_](#no-unidirectional-flow-worries)
|
||||
|
@ -331,6 +337,7 @@ a#oninit
|
|||
### _OnInit()_
|
||||
|
||||
Use `ngOnInit()` for two main reasons:
|
||||
|
||||
1. To perform complex initializations shortly after construction.
|
||||
1. To set up the component after Angular sets the input properties.
|
||||
|
||||
|
@ -522,11 +529,14 @@ figure.image-display
|
|||
.l-sub-section
|
||||
:marked
|
||||
The telltale signs of *content projection* are twofold:
|
||||
- HTML between component element tags.
|
||||
- The presence of `<ng-content>` tags in the component's template.
|
||||
|
||||
* HTML between component element tags.
|
||||
* The presence of `<ng-content>` tags in the component's template.
|
||||
|
||||
a#aftercontent-hooks
|
||||
:marked
|
||||
### AfterContent hooks
|
||||
|
||||
*AfterContent* hooks are similar to the *AfterView* hooks.
|
||||
The key difference is in the child component.
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ style.
|
|||
|
||||
### Table of contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [The problem](#problem)
|
||||
- [Troublesome CSS](#CSS)
|
||||
- [Restrictive layout](#restrictive-layout)
|
||||
- [<ng-container> is excluded from DOM](#excluded)
|
||||
- [<ng-container> is not <ng-content>](#ng-content)
|
||||
- [Summary](#summary)
|
||||
* [Introduction](#introduction)
|
||||
* [The problem](#problem)
|
||||
* [Troublesome CSS](#CSS)
|
||||
* [Restrictive layout](#restrictive-layout)
|
||||
* [<ng-container> is excluded from DOM](#excluded)
|
||||
* [<ng-container> is not <ng-content>](#ng-content)
|
||||
* [Summary](#summary)
|
||||
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
|
|
|
@ -25,9 +25,11 @@ block includes
|
|||
## Table of Contents
|
||||
|
||||
<!-- CF: The titling for tables of contents in the advanced chapters is inconsistent:
|
||||
- some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2
|
||||
- some headings are H2, some are H3
|
||||
- some pages don't have tables of contents
|
||||
|
||||
* some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2
|
||||
* some headings are H2, some are H3
|
||||
* some pages don't have tables of contents
|
||||
|
||||
I didn't make changes here as I'm not sure what the correct style is.
|
||||
-->
|
||||
|
||||
|
@ -351,6 +353,7 @@ a#imports
|
|||
`ngModel` is the selector for the `NgModel` directive.
|
||||
|
||||
Although `NgModel` is an Angular directive, the _Angular compiler_ won't recognize it for the following reasons:
|
||||
|
||||
* `AppModule` doesn't declare `NgModel`.
|
||||
* `NgModel` wasn't imported via `BrowserModule`.
|
||||
|
||||
|
@ -619,9 +622,11 @@ a#feature-modules
|
|||
### Improvements
|
||||
:marked
|
||||
There's a lot to like in the revised `AppModule`.
|
||||
|
||||
* It does not change as the _Contact_ domain grows.
|
||||
* It only changes when you add new modules.
|
||||
* It's simpler:
|
||||
|
||||
* Fewer import statements.
|
||||
* No `FormsModule` import.
|
||||
* No contact-specific declarations.
|
||||
|
@ -818,6 +823,7 @@ a#shared-module
|
|||
and share them with the modules that need them.
|
||||
|
||||
1. Create an `src/app/shared` folder.
|
||||
|
||||
* Move the `AwesomePipe` and `HighlightDirective` from `src/app/contact` to `src/app/shared`.
|
||||
* Delete the `HighlightDirective` classes from `src/app/` and `src/app/hero`.
|
||||
* Create a `SharedModule` class to own the shared material.
|
||||
|
@ -827,6 +833,7 @@ a#shared-module
|
|||
+makeExample('ngmodule/ts/src/app/shared/shared.module.ts', '', 'src/app/src/app/shared/shared.module.ts')
|
||||
:marked
|
||||
Note the following:
|
||||
|
||||
* It imports the `CommonModule` because its component needs common directives.
|
||||
* It declares and exports the utility pipe, directive, and component classes as expected.
|
||||
* It re-exports the `CommonModule` and `FormsModule`
|
||||
|
@ -954,6 +961,7 @@ a#core-module
|
|||
|
||||
:marked
|
||||
`AppModule` now has the following qualities:
|
||||
|
||||
* A little smaller because many `src/app/root` classes have moved to other modules.
|
||||
* Stable because you'll add future components and providers to other modules, not this one.
|
||||
* Delegated to imported modules rather than doing work.
|
||||
|
@ -970,6 +978,7 @@ a#core-module
|
|||
|
||||
:marked
|
||||
Notice the following:
|
||||
|
||||
* The `AwesomePipe` and `HighlightDirective` are gone.
|
||||
* The imports include `SharedModule` instead of `CommonModule` and `FormsModule`.
|
||||
* The new version is leaner and cleaner.
|
||||
|
@ -987,6 +996,7 @@ a#core-for-root
|
|||
It takes a service configuration object and returns a
|
||||
[ModuleWithProviders](../api/core/index/ModuleWithProviders-interface.html), which is
|
||||
a simple object with the following properties:
|
||||
|
||||
* `ngModule`: the `CoreModule` class
|
||||
* `providers`: the configured providers
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ block includes
|
|||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle")
|
||||
:marked
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -386,6 +385,7 @@ figure.image-display
|
|||
|
||||
:marked
|
||||
A breakpoint on the pipe's request for data shows the following:
|
||||
|
||||
* Each binding gets its own pipe instance.
|
||||
* Each pipe instance caches its own URL and data.
|
||||
* Each pipe instance only calls the server once.
|
||||
|
|
|
@ -8,23 +8,23 @@ a#toc
|
|||
:marked
|
||||
## Contents
|
||||
|
||||
- [Introduction to reactive forms](#intro)
|
||||
- [Setup](#setup)
|
||||
- [Create a data model](#data-model)
|
||||
- [Create a _reactive forms_ component](#create-component)
|
||||
- [Create its template file](#create-template)
|
||||
- [Import the _ReactiveFormsModule_](#import)
|
||||
- [Display the _HeroDetailComponent_](#update)
|
||||
- [Add a FormGroup](#formgroup)
|
||||
- [Taking a look at the form model](#json)
|
||||
- [Introduction to _FormBuilder_](#formbuilder)
|
||||
- [Validators.required](#validators)
|
||||
- [Nested FormGroups](#grouping)
|
||||
- [Inspect _FormControl_ properties](#properties)
|
||||
- [Set form model data using _setValue_ and _patchValue_](#set-data)
|
||||
- [Use _FormArray_ to present an array of _FormGroups_](#form-array)
|
||||
- [Observe control changes](#observe-control)
|
||||
- [Save form data](#save)
|
||||
* [Introduction to reactive forms](#intro)
|
||||
* [Setup](#setup)
|
||||
* [Create a data model](#data-model)
|
||||
* [Create a _reactive forms_ component](#create-component)
|
||||
* [Create its template file](#create-template)
|
||||
* [Import the _ReactiveFormsModule_](#import)
|
||||
* [Display the _HeroDetailComponent_](#update)
|
||||
* [Add a FormGroup](#formgroup)
|
||||
* [Taking a look at the form model](#json)
|
||||
* [Introduction to _FormBuilder_](#formbuilder)
|
||||
* [Validators.required](#validators)
|
||||
* [Nested FormGroups](#grouping)
|
||||
* [Inspect _FormControl_ properties](#properties)
|
||||
* [Set form model data using _setValue_ and _patchValue_](#set-data)
|
||||
* [Use _FormArray_ to present an array of _FormGroups_](#form-array)
|
||||
* [Observe control changes](#observe-control)
|
||||
* [Save form data](#save)
|
||||
|
||||
Try the <live-example plnkr="final" title="Reactive Forms (final) in Plunker">Reactive Forms live-example</live-example>.
|
||||
|
||||
|
@ -229,6 +229,7 @@ a#import
|
|||
|
||||
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
|
||||
the `ReactiveFormsModule` and the `HeroDetailComponent`.
|
||||
1. Add `ReactiveFormsModule` to the `AppModule`'s `imports` list.
|
||||
|
@ -1017,14 +1018,14 @@ figure.image-display
|
|||
|
||||
This page covered:
|
||||
|
||||
- How to create a reactive form component and its corresponding template.
|
||||
- How to use `FormBuilder` to simplify coding a reactive form.
|
||||
- Grouping `FormControls`.
|
||||
- Inspecting `FormControl` properties.
|
||||
- Setting data with `patchValue` and `setValue`.
|
||||
- Adding groups dynamically with `FormArray`.
|
||||
- Observing changes to the value of a `FormControl`.
|
||||
- Saving form changes.
|
||||
* How to create a reactive form component and its corresponding template.
|
||||
* How to use `FormBuilder` to simplify coding a reactive form.
|
||||
* Grouping `FormControls`.
|
||||
* Inspecting `FormControl` properties.
|
||||
* Setting data with `patchValue` and `setValue`.
|
||||
* Adding groups dynamically with `FormArray`.
|
||||
* Observing changes to the value of a `FormControl`.
|
||||
* Saving form changes.
|
||||
|
||||
a#source-code
|
||||
:marked
|
||||
|
|
|
@ -33,84 +33,100 @@ include ../../../_includes/_see-addr-bar
|
|||
# Contents
|
||||
|
||||
* [The Basics](#basics)
|
||||
- [`<base href>`](#basics-base-href)
|
||||
- [Router imports](#basics-router-imports)
|
||||
- [Configuration](#basics-config)
|
||||
- [Router outlet](#basics-router-outlet)
|
||||
- [Router links](#basics-router-links)
|
||||
- [Router state](#basics-router-state)
|
||||
- [Summary](#basics-summary)
|
||||
|
||||
* [`<base href>`](#basics-base-href)
|
||||
* [Router imports](#basics-router-imports)
|
||||
* [Configuration](#basics-config)
|
||||
* [Router outlet](#basics-router-outlet)
|
||||
* [Router links](#basics-router-links)
|
||||
* [Router state](#basics-router-state)
|
||||
* [Summary](#basics-summary)
|
||||
|
||||
* [The sample application](#sample-app-intro)
|
||||
|
||||
* [Milestone 1: Getting started with the router](#getting-started)
|
||||
- [Setting the base href](#base-href)
|
||||
- [Importing from the router library](#import)
|
||||
- [Define routes](#route-config)
|
||||
- [The `AppComponent` shell](#shell)
|
||||
- [RouterOutlet](#router-outlet)
|
||||
- [`RouterLink binding`](#router-link)
|
||||
- [`RouterLinkActive` binding](#router-link-active)
|
||||
- [Wildcard route](#wildcard)
|
||||
- [The default route to heroes](#default-route)
|
||||
|
||||
* [Setting the base href](#base-href)
|
||||
* [Importing from the router library](#import)
|
||||
* [Define routes](#route-config)
|
||||
* [The `AppComponent` shell](#shell)
|
||||
* [RouterOutlet](#router-outlet)
|
||||
* [`RouterLink binding`](#router-link)
|
||||
* [`RouterLinkActive` binding](#router-link-active)
|
||||
* [Wildcard route](#wildcard)
|
||||
* [The default route to heroes](#default-route)
|
||||
|
||||
* [Milestone 2: Routing module](#routing-module)
|
||||
- [Refactor the routing configuration into a routing module](#routing-refactor)
|
||||
- [Do you need a Routing Module?](#why-routing-module)
|
||||
|
||||
* [Refactor the routing configuration into a routing module](#routing-refactor)
|
||||
* [Do you need a Routing Module?](#why-routing-module)
|
||||
|
||||
* [Milestone 3: Heroes feature](#heroes-feature)
|
||||
- [Add heroes functionality](#heroes-functionality)
|
||||
- [Hero feature routing requirements](#hero-routing-requirements)
|
||||
- [Hero feature route configuration](#hero-routing-module)
|
||||
- [Add the routing module to the _HeroesModule_](#adding-routing-module)
|
||||
- [Remove duplicate hero routes](#remove-duplicate-hero-routes)
|
||||
- [Import hero module into AppModule](#merge-hero-routes)
|
||||
- [Module import order matters](#routing-module-order)
|
||||
- [Route Definition with a parameter](#route-def-with-parameter)
|
||||
- [Navigate to hero detail imperatively](#navigate)
|
||||
- [Setting the route parameters in the list view](#route-parameters)
|
||||
- [ActivatedRoute: the one-stop-shop for route information](#activated-route)
|
||||
- [Observable params and component reuse](#reuse)
|
||||
- [Snapshot: the _no-observable_ alternative](#snapshot)
|
||||
- [Navigating back to the list component](#nav-to-list)
|
||||
- [Route Parameters: Required or optional?](#optional-route-parameters)
|
||||
- [Heroes list: optionally selecting a hero](#optionally-selecting)
|
||||
- [Route parameters in the *ActivatedRoute* service](#route-parameters-activated-route)
|
||||
- [Adding animations to the routed component](#route-animation)
|
||||
- [Milestone 3 wrap up](#milestone-3-wrap-up)
|
||||
|
||||
* [Add heroes functionality](#heroes-functionality)
|
||||
* [Hero feature routing requirements](#hero-routing-requirements)
|
||||
* [Hero feature route configuration](#hero-routing-module)
|
||||
* [Add the routing module to the _HeroesModule_](#adding-routing-module)
|
||||
* [Remove duplicate hero routes](#remove-duplicate-hero-routes)
|
||||
* [Import hero module into AppModule](#merge-hero-routes)
|
||||
* [Module import order matters](#routing-module-order)
|
||||
* [Route Definition with a parameter](#route-def-with-parameter)
|
||||
* [Navigate to hero detail imperatively](#navigate)
|
||||
* [Setting the route parameters in the list view](#route-parameters)
|
||||
* [ActivatedRoute: the one-stop-shop for route information](#activated-route)
|
||||
* [Observable params and component reuse](#reuse)
|
||||
* [Snapshot: the _no-observable_ alternative](#snapshot)
|
||||
* [Navigating back to the list component](#nav-to-list)
|
||||
* [Route Parameters: Required or optional?](#optional-route-parameters)
|
||||
* [Heroes list: optionally selecting a hero](#optionally-selecting)
|
||||
* [Route parameters in the *ActivatedRoute* service](#route-parameters-activated-route)
|
||||
* [Adding animations to the routed component](#route-animation)
|
||||
* [Milestone 3 wrap up](#milestone-3-wrap-up)
|
||||
|
||||
* [Milestone 4: Crisis center feature](#milestone-4)
|
||||
- [A crisis center with child routes](#crisis-child-routes)
|
||||
- [Child routing component](#child-routing-component)
|
||||
- [Child route configuration](#child-route-config)
|
||||
- [Import crisis center module into the _AppModule_ routes](#import-crisis-module)
|
||||
- [Relative navigation](#relative-navigation)
|
||||
- [Navigate to crisis detail with a relative URL](#nav-to-crisis)
|
||||
- [Displaying multiple routes in named outlets](#named-outlets)
|
||||
- [Secondary routes](#secondary-routes)
|
||||
- [Add a secondary route](#add-secondary-route)
|
||||
- [Secondary route navigation: merging routes during navigation](#secondary-route-navigation)
|
||||
- [Clearing secondary routes](#clear-secondary-routes)
|
||||
|
||||
* [A crisis center with child routes](#crisis-child-routes)
|
||||
* [Child routing component](#child-routing-component)
|
||||
* [Child route configuration](#child-route-config)
|
||||
* [Import crisis center module into the _AppModule_ routes](#import-crisis-module)
|
||||
* [Relative navigation](#relative-navigation)
|
||||
* [Navigate to crisis detail with a relative URL](#nav-to-crisis)
|
||||
* [Displaying multiple routes in named outlets](#named-outlets)
|
||||
* [Secondary routes](#secondary-routes)
|
||||
* [Add a secondary route](#add-secondary-route)
|
||||
* [Secondary route navigation: merging routes during navigation](#secondary-route-navigation)
|
||||
* [Clearing secondary routes](#clear-secondary-routes)
|
||||
|
||||
* [Milestone 5: Route guards](#guards)
|
||||
- [`CanActivate`: requiring authentication](#can-activate-guard)
|
||||
- [Component-less route: grouping routes without a component](#component-less-route)
|
||||
- [Guard the admin feature](#guard-admin-feature)
|
||||
- [Teach *AuthGuard* to authenticate](#teach-auth)
|
||||
- [Add the login component](#add-login-component)
|
||||
- [`CanActivateChild`: guarding child routes](#can-activate-child-guard)
|
||||
- [`CanDeactivate`: handling unsaved changes](#can-deactivate-guard)
|
||||
- [Cancel and save](#cancel-save)
|
||||
- [`Resolve`: pre-fetching component data](#resolve-guard)
|
||||
- [Fetch data before navigating](#fetch-before-navigating )
|
||||
- [Query parameters and fragments](#query-parameters)
|
||||
|
||||
* [`CanActivate`: requiring authentication](#can-activate-guard)
|
||||
* [Component-less route: grouping routes without a component](#component-less-route)
|
||||
* [Guard the admin feature](#guard-admin-feature)
|
||||
* [Teach *AuthGuard* to authenticate](#teach-auth)
|
||||
* [Add the login component](#add-login-component)
|
||||
* [`CanActivateChild`: guarding child routes](#can-activate-child-guard)
|
||||
* [`CanDeactivate`: handling unsaved changes](#can-deactivate-guard)
|
||||
* [Cancel and save](#cancel-save)
|
||||
* [`Resolve`: pre-fetching component data](#resolve-guard)
|
||||
* [Fetch data before navigating](#fetch-before-navigating )
|
||||
* [Query parameters and fragments](#query-parameters)
|
||||
|
||||
* [Milestone 6: Asynchronous routing](#asynchronous-routing)
|
||||
- [Lazy loading route configuration](#lazy-loading-route-config)
|
||||
- [CanLoad Guard: guarding unauthorized loading of feature modules](#can-load-guard)
|
||||
- [Preloading: background loading of feature areas](#preloading)
|
||||
- [How preloading works](#how-preloading)
|
||||
- [Lazy load the crisis center](#lazy-load-crisis-center)
|
||||
- [_CanLoad_ blocks preload](#preload-canload)
|
||||
- [Custom Preloading Strategy](#custom-preloading)
|
||||
- [Inspect the router's configuration](#inspect-config)
|
||||
- [Wrap up and final app](#final-app)
|
||||
|
||||
* [Lazy loading route configuration](#lazy-loading-route-config)
|
||||
* [CanLoad Guard: guarding unauthorized loading of feature modules](#can-load-guard)
|
||||
* [Preloading: background loading of feature areas](#preloading)
|
||||
* [How preloading works](#how-preloading)
|
||||
* [Lazy load the crisis center](#lazy-load-crisis-center)
|
||||
* [_CanLoad_ blocks preload](#preload-canload)
|
||||
* [Custom Preloading Strategy](#custom-preloading)
|
||||
* [Inspect the router's configuration](#inspect-config)
|
||||
* [Wrap up and final app](#final-app)
|
||||
|
||||
* [Appendices](#appendices)
|
||||
- [Appendix: link parameters array](#link-parameters-array)
|
||||
- [Appendix: *LocationStrategy* and browser URL styles](#location-strategy)
|
||||
|
||||
* [Appendix: link parameters array](#link-parameters-array)
|
||||
* [Appendix: *LocationStrategy* and browser URL styles](#location-strategy)
|
||||
|
||||
|
||||
|
||||
|
@ -491,10 +507,10 @@ a#route-config
|
|||
|
||||
In simpler terms, you might say this of the first route:
|
||||
|
||||
- When the browser's location URL changes to match the path segment `/crisis-center`, then
|
||||
* When the browser's location URL changes to match the path segment `/crisis-center`, then
|
||||
the router activates an instance of the `CrisisListComponent` and displays its view.
|
||||
|
||||
- When the application requests navigation to the path `/crisis-center`, the router
|
||||
* When the application requests navigation to the path `/crisis-center`, the router
|
||||
activates an instance of `CrisisListComponent`, displays its view, and updates the
|
||||
browser's address location and history with the URL for that path.
|
||||
|
||||
|
@ -758,6 +774,7 @@ a#redirect
|
|||
We recommend moving the routing information into a special-purpose module called a *Routing Module*.
|
||||
|
||||
The **Routing Module** has several characteristics:
|
||||
|
||||
* Separates routing concerns from other application concerns.
|
||||
* Provides a module to replace or remove when testing the application.
|
||||
* Provides a well-known location for routing service providers including guards and resolvers.
|
||||
|
@ -856,19 +873,21 @@ a#heroes-functionality
|
|||
|
||||
Follow these steps:
|
||||
|
||||
- Create the `src/app/heroes` folder; you'll be adding files implementing *hero management* there.
|
||||
- Delete the placeholder `hero-list.component.ts` that's in the `app` folder.
|
||||
- Create a new `hero-list.component.ts` under `src/app/heroes`.
|
||||
- Copy into it the contents of the `app.component.ts` from
|
||||
* Create the `src/app/heroes` folder; you'll be adding files implementing *hero management* there.
|
||||
* Delete the placeholder `hero-list.component.ts` that's in the `app` folder.
|
||||
* Create a new `hero-list.component.ts` under `src/app/heroes`.
|
||||
* Copy into it the contents of the `app.component.ts` from
|
||||
the <live-example name="toh-4" title="Tour of Heroes: Services example code">"Services" tutorial</live-example>.
|
||||
- Make a few minor but necessary changes:
|
||||
- Delete the `selector` (routed components don't need them).
|
||||
- Delete the `<h1>`.
|
||||
- Relabel the `<h2>` to `<h2>HEROES</h2>`.
|
||||
- Delete the `<hero-detail>` at the bottom of the template.
|
||||
- Rename the `AppComponent` class to `HeroListComponent`.
|
||||
- Copy the `hero-detail.component.ts` and the `hero.service.ts` files into the `heroes` subfolder.
|
||||
- Create a (pre-routing) `heroes.module.ts` in the heroes folder that looks like this:
|
||||
* Make a few minor but necessary changes:
|
||||
|
||||
* Delete the `selector` (routed components don't need them).
|
||||
* Delete the `<h1>`.
|
||||
* Relabel the `<h2>` to `<h2>HEROES</h2>`.
|
||||
* Delete the `<hero-detail>` at the bottom of the template.
|
||||
* Rename the `AppComponent` class to `HeroListComponent`.
|
||||
|
||||
* Copy the `hero-detail.component.ts` and the `hero.service.ts` files into the `heroes` subfolder.
|
||||
* Create a (pre-routing) `heroes.module.ts` in the heroes folder that looks like this:
|
||||
|
||||
+makeExample('src/app/heroes/heroes.module.ts', 'v1','src/app/heroes/heroes.module.ts (pre-routing)')
|
||||
|
||||
|
@ -1542,10 +1561,10 @@ a#milestone-4
|
|||
|
||||
Begin by imitating the heroes feature:
|
||||
|
||||
- Delete the placeholder crisis center file.
|
||||
- Create an `app/crisis-center` folder.
|
||||
- Copy the files from `app/heroes` into the new crisis center folder.
|
||||
- In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises".
|
||||
* Delete the placeholder crisis center file.
|
||||
* Create an `app/crisis-center` folder.
|
||||
* Copy the files from `app/heroes` into the new crisis center folder.
|
||||
* In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises".
|
||||
|
||||
You'll turn the `CrisisService` into a purveyor of mock crises instead of mock heroes:
|
||||
|
||||
|
@ -1591,9 +1610,9 @@ a#child-routing-component
|
|||
:marked
|
||||
The `CrisisCenterComponent` has the following in common with the `AppComponent`:
|
||||
|
||||
- It is the *root* of the crisis center area,
|
||||
* It is the *root* of the crisis center area,
|
||||
just as `AppComponent` is the root of the entire application.
|
||||
- It is a *shell* for the crisis management feature area,
|
||||
* It is a *shell* for the crisis management feature area,
|
||||
just as the `AppComponent` is a shell to manage the high-level workflow.
|
||||
|
||||
Like most shells, the `CrisisCenterComponent` class is very simple, simpler even than `AppComponent`:
|
||||
|
@ -1871,6 +1890,7 @@ code-example.
|
|||
|
||||
:marked
|
||||
The interesting part of the URL follows the `...`:
|
||||
|
||||
* The `crisis-center` is the primary navigation.
|
||||
* Parentheses surround the secondary route.
|
||||
* The secondary route consists of an outlet name (`popup`), a `colon` separator, and the secondary route path (`compose`).
|
||||
|
@ -2364,6 +2384,7 @@ a#fetch-before-navigating
|
|||
|
||||
:marked
|
||||
**Two critical points**
|
||||
|
||||
1. The router's `Resolve` interface is optional.
|
||||
The `CrisisDetailResolver` doesn't inherit from a base class.
|
||||
The router looks for that method and calls it if found.
|
||||
|
@ -2684,6 +2705,7 @@ a#custom-preloading
|
|||
`SelectivePreloadingStrategy` implements the `PreloadingStrategy`, which has one method, `preload`.
|
||||
|
||||
The router calls the `preload` method with two arguments:
|
||||
|
||||
1. The route to consider.
|
||||
1. A loader function that can load the routed module asynchronously.
|
||||
|
||||
|
@ -2699,12 +2721,14 @@ a#custom-preloading
|
|||
Shortly, you'll extend the `AdminDashboardComponent` to inject this service and display its `preloadedModules` array.
|
||||
|
||||
But first, make a few changes to the `AppRoutingModule`.
|
||||
|
||||
1. Import `SelectivePreloadingStrategy` into `AppRoutingModule`.
|
||||
1. Replace the `PreloadAllModules` strategy in the call to `forRoot` with this `SelectivePreloadingStrategy`.
|
||||
1. Add the `SelectivePreloadingStrategy` strategy to the `AppRoutingModule` providers array so it can be injected
|
||||
elsewhere in the app.
|
||||
|
||||
Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
|
||||
|
||||
1. Import the `SelectivePreloadingStrategy` (it's a service).
|
||||
1. Inject it into the dashboard's constructor.
|
||||
1. Update the template to display the strategy service's `preloadedModules` array.
|
||||
|
|
|
@ -19,30 +19,38 @@ block includes
|
|||
* [Demos](#demos)
|
||||
* [Providing HTTP Services](#http-providers)
|
||||
* [The Tour of Heroes *HTTP* client demo](#http-client)
|
||||
- [The `HeroListComponent` class](#HeroListComponent)
|
||||
|
||||
* [The `HeroListComponent` class](#HeroListComponent)
|
||||
|
||||
* [Fetch data with `http.get()`](#fetch-data)
|
||||
<li>[RxJS library](#rxjs-library)
|
||||
<ul>
|
||||
<li> [Enable RxJS operators](#enable-rxjs-operators)</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
* [Process the response object](#extract-data)
|
||||
- [Parse to `JSON`](#parse-to-json)
|
||||
- [Do not return the response object](#no-return-response-object)
|
||||
- [Always handle errors](#error-handling)
|
||||
- [`HeroListComponent` error handling](#hero-list-component)
|
||||
|
||||
* [Parse to `JSON`](#parse-to-json)
|
||||
* [Do not return the response object](#no-return-response-object)
|
||||
* [Always handle errors](#error-handling)
|
||||
* [`HeroListComponent` error handling](#hero-list-component)
|
||||
|
||||
* [Send data to the server](#update)
|
||||
- [Headers](#headers)
|
||||
- [JSON results](#json-results)
|
||||
|
||||
* [Headers](#headers)
|
||||
* [JSON results](#json-results)
|
||||
|
||||
<ul><li> [Fall back to promises](#promises)</ul>
|
||||
|
||||
* [Cross-Origin Requests: Wikipedia example](#cors)
|
||||
|
||||
<ul>
|
||||
<li> [Search Wikipedia](#search-wikipedia)</li>
|
||||
<li> [Search parameters](#search-parameters)</li>
|
||||
<li> [The WikiComponent](#wikicomponent)</li>
|
||||
</ul>
|
||||
|
||||
* [A wasteful app](#wasteful-app)
|
||||
<li> [More fun with Observables](#more-observables)
|
||||
<ul>
|
||||
|
@ -50,6 +58,7 @@ block includes
|
|||
<li> [Listen for search terms](#listen-for-search-terms)</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
* [Guarding against Cross-Site Request Forgery](#xsrf)
|
||||
* [Override default request headers (and other request options)](#override-default-request-options)
|
||||
* [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api)
|
||||
|
@ -65,10 +74,10 @@ a#demos
|
|||
|
||||
block demos-list
|
||||
:marked
|
||||
- [The Tour of Heroes *HTTP* client demo](#http-client).
|
||||
- [Fall back to Promises](#promises).
|
||||
- [Cross-Origin Requests: Wikipedia example](#cors).
|
||||
- [More fun with Observables](#more-observables).
|
||||
* [The Tour of Heroes *HTTP* client demo](#http-client).
|
||||
* [Fall back to Promises](#promises).
|
||||
* [Cross-Origin Requests: Wikipedia example](#cors).
|
||||
* [More fun with Observables](#more-observables).
|
||||
|
||||
:marked
|
||||
The root `AppComponent` orchestrates these demos:
|
||||
|
|
|
@ -15,6 +15,7 @@ a#develop-locally
|
|||
:marked
|
||||
Make sure you have [node and npm installed](#install-prerequisites "What if you don't have node and npm?").
|
||||
Then ...
|
||||
|
||||
1. Create a project folder (you can call it `quickstart` and rename it later).
|
||||
1. [Clone](#clone "Clone it from github") or [download](#download "download it from github") the **QuickStart seed** into your project folder.
|
||||
1. Install [npm](#install-prerequisites "What if you don't have node and npm?") packages.
|
||||
|
@ -206,6 +207,7 @@ a#why-locally
|
|||
The other samples are based on the QuickStart seed.
|
||||
|
||||
As much fun as this is ...
|
||||
|
||||
* you can't ship your app in plunker
|
||||
* you aren't always online when writing code
|
||||
* transpiling TypeScript in the browser is slow
|
||||
|
|
|
@ -12,18 +12,20 @@ style.
|
|||
|
||||
### Table of contents
|
||||
|
||||
- [What are structural directives?](#definition)
|
||||
- [*NgIf* case study](#ngIf)
|
||||
- [The asterisk (*) prefix](#asterisk)
|
||||
- [Inside *NgFor*](#ngFor)
|
||||
- [microsyntax](#microsyntax)
|
||||
- [template input variables](#template-input-variable)
|
||||
- [one structural directive per element](#one-per-element)
|
||||
- [Inside the *NgSwitch* directives](#ngSwitch)
|
||||
- [Prefer the (*) prefix](#prefer-asterisk)
|
||||
- [The <template> element](#template)
|
||||
- [Group sibling elements with <ng-container>](#ng-container)
|
||||
- [Write a structural directive](#unless)
|
||||
* [What are structural directives?](#definition)
|
||||
* [*NgIf* case study](#ngIf)
|
||||
* [The asterisk (*) prefix](#asterisk)
|
||||
* [Inside *NgFor*](#ngFor)
|
||||
|
||||
* [microsyntax](#microsyntax)
|
||||
* [template input variables](#template-input-variable)
|
||||
* [one structural directive per element](#one-per-element)
|
||||
|
||||
* [Inside the *NgSwitch* directives](#ngSwitch)
|
||||
* [Prefer the (*) prefix](#prefer-asterisk)
|
||||
* [The <template> element](#template)
|
||||
* [Group sibling elements with <ng-container>](#ng-container)
|
||||
* [Write a structural directive](#unless)
|
||||
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
|
|
|
@ -31,18 +31,25 @@ a#toc
|
|||
* [Two-way data binding ( <span class="syntax">[(...)]</span> )](#two-way)
|
||||
* [Built-in directives](#directives)
|
||||
* [Built-in attribute directives](#attribute-directives)
|
||||
|
||||
* [NgClass](#ngClass)
|
||||
* [NgStyle](#ngStyle)
|
||||
* [NgModel (<span class="syntax">[(ngModel)]</span>) ](#ngModel)
|
||||
|
||||
* [Built-in structural directives](#structural-directives)
|
||||
|
||||
* [NgIf](#ngIf)
|
||||
* [NgFor](#ngFor)
|
||||
|
||||
* [Template input variables](#template-input-variables)
|
||||
* [Microsyntax](#microsyntax)
|
||||
|
||||
* [The NgSwitch directives](#ngSwitch)
|
||||
|
||||
* [Template reference variables ( <span class="syntax">#var</span> )](#ref-vars)
|
||||
* [Input and output properties ( <span class="syntax">@Input</span> and <span class="syntax">@Output</span> )](#inputs-outputs)
|
||||
* [Template expression operators](#expression-operators)
|
||||
|
||||
* [pipe ( <span class="syntax">|</span> )](#pipe)
|
||||
* [safe navigation operator ( <span class="syntax">?.</span> )](#safe-navigation-operator)
|
||||
|
||||
|
@ -653,6 +660,7 @@ a#one-time-initialization
|
|||
### One-time string initialization
|
||||
|
||||
You *should* omit the brackets when all of the following are true:
|
||||
|
||||
* The target property accepts a string value.
|
||||
* The string is a fixed value that you can bake into the template.
|
||||
* This initial value never changes.
|
||||
|
@ -1463,6 +1471,7 @@ a#trackBy
|
|||
Here is an illustration of the _trackBy_ effect.
|
||||
"Reset heroes" creates new heroes with the same `hero.id`s.
|
||||
"Change ids" creates new heroes with new `hero.id`s.
|
||||
|
||||
* With no `trackBy`, both buttons trigger complete DOM element replacement.
|
||||
* With `trackBy`, only changing the `id` triggers element replacement.
|
||||
|
||||
|
|
|
@ -13,83 +13,109 @@ a#top
|
|||
* [Live examples](#live-examples "Live examples of the tests in this guide")
|
||||
<br><br>
|
||||
* [Introduction to Angular testing](#testing-intro)
|
||||
- [Tools and technologies](#tools-and-tech)
|
||||
- [Setup](#setup)
|
||||
- [Isolated unit tests vs. the Angular testing utilities](#isolated-v-testing-utilities)
|
||||
|
||||
* [Tools and technologies](#tools-and-tech)
|
||||
* [Setup](#setup)
|
||||
* [Isolated unit tests vs. the Angular testing utilities](#isolated-v-testing-utilities)
|
||||
|
||||
* [The first karma test](#1st-karma-test)
|
||||
- [Run with karma](#run-karma)
|
||||
- [Test debugging](#test-debugging)
|
||||
- [Try the live example](#live-karma-example)
|
||||
|
||||
* [Run with karma](#run-karma)
|
||||
* [Test debugging](#test-debugging)
|
||||
* [Try the live example](#live-karma-example)
|
||||
|
||||
* [Test a component](#simple-component-test)
|
||||
- [_TestBed_](#testbed)
|
||||
- [_createComponent_](#create-component)
|
||||
- [_ComponentFixture_, _DebugElement_, and _query(By.css)_](#component-fixture)
|
||||
- [The tests](#the-tests)
|
||||
- [_detectChanges_: Angular change detection within a test](#detect-changes)
|
||||
- [Try the live example](#try-example)
|
||||
- [Automatic change detection](#auto-detect-changes)
|
||||
|
||||
* [_TestBed_](#testbed)
|
||||
* [_createComponent_](#create-component)
|
||||
* [_ComponentFixture_, _DebugElement_, and _query(By.css)_](#component-fixture)
|
||||
* [The tests](#the-tests)
|
||||
* [_detectChanges_: Angular change detection within a test](#detect-changes)
|
||||
* [Try the live example](#try-example)
|
||||
* [Automatic change detection](#auto-detect-changes)
|
||||
|
||||
* [Test a component with an external template](#component-with-external-template)
|
||||
- [The first asynchronous _beforeEach_](#async-in-before-each)
|
||||
- [_compileComponents_](#compile-components)
|
||||
- [The second synchronous _beforeEach_](#second-before-each)
|
||||
- [Waiting for _compileComponents_](#waiting-compile-components)
|
||||
- [Try the live example](#live-external-template-example)
|
||||
|
||||
* [The first asynchronous _beforeEach_](#async-in-before-each)
|
||||
* [_compileComponents_](#compile-components)
|
||||
* [The second synchronous _beforeEach_](#second-before-each)
|
||||
* [Waiting for _compileComponents_](#waiting-compile-components)
|
||||
* [Try the live example](#live-external-template-example)
|
||||
|
||||
* [Test a component with a service dependency](#component-with-dependency)
|
||||
- [Provide service test doubles](#service-test-doubles)
|
||||
- [Get injected services](#get-injected-service)
|
||||
- [_TestBed.get_](#testbed-get)
|
||||
- [Always get the service from an injector](#service-from-injector)
|
||||
- [Final setup and tests](#welcome-spec-setup)
|
||||
|
||||
* [Provide service test doubles](#service-test-doubles)
|
||||
* [Get injected services](#get-injected-service)
|
||||
* [_TestBed.get_](#testbed-get)
|
||||
* [Always get the service from an injector](#service-from-injector)
|
||||
* [Final setup and tests](#welcome-spec-setup)
|
||||
|
||||
* [Test a component with an async service](#component-with-async-service)
|
||||
- [Spying on the real service](#service-spy)
|
||||
- [Synchronous tests](#sync-tests)
|
||||
- [The _async_ funciton in it](#async)
|
||||
- [_whenStable_](#when-stable)
|
||||
- [The _fakeAsync_ function](#fake-async)
|
||||
- [The _tick_ function](#tick)
|
||||
- [_jasmine.done_](#jasmine-done)
|
||||
|
||||
* [Spying on the real service](#service-spy)
|
||||
* [Synchronous tests](#sync-tests)
|
||||
* [The _async_ funciton in it](#async)
|
||||
* [_whenStable_](#when-stable)
|
||||
* [The _fakeAsync_ function](#fake-async)
|
||||
* [The _tick_ function](#tick)
|
||||
* [_jasmine.done_](#jasmine-done)
|
||||
|
||||
* [Test a component with inputs and outputs](#component-with-input-output)
|
||||
- [Test _DashboardHeroComponent_ stand-alone](#dashboard-standalone)
|
||||
- [_triggerEventHandler_](#trigger-event-handler)
|
||||
|
||||
* [Test _DashboardHeroComponent_ stand-alone](#dashboard-standalone)
|
||||
* [_triggerEventHandler_](#trigger-event-handler)
|
||||
|
||||
* [Test a component inside a test host component](#component-inside-test-host)
|
||||
<br><br>
|
||||
|
||||
* [Test a routed component](#routed-component)
|
||||
- [The _inject_ helper function](#inject)
|
||||
- [Test a routed component with parameters](#routed-component-w-param)
|
||||
- [Create an _Observable_ test double](#stub-observable)
|
||||
- [Testing with the _Observable_ test double](#tests-w-observable-double)
|
||||
|
||||
* [The _inject_ helper function](#inject)
|
||||
* [Test a routed component with parameters](#routed-component-w-param)
|
||||
* [Create an _Observable_ test double](#stub-observable)
|
||||
* [Testing with the _Observable_ test double](#tests-w-observable-double)
|
||||
|
||||
* [Use a _page_ object to simplify setup](#page-object)
|
||||
* [Set up with module imports](#import-module)
|
||||
* [Import the feature module](#feature-module-import)
|
||||
<br><br>
|
||||
* [Override a component's providers](#component-override)
|
||||
- [The _overrideComponent_ method](#override-component-method)
|
||||
- [Provide a _spy-stub (HeroDetailServiceSpy)_](#spy-stub)
|
||||
- [The override tests](#override-tests)
|
||||
- [More overrides](#more-overrides)
|
||||
|
||||
* [The _overrideComponent_ method](#override-component-method)
|
||||
* [Provide a _spy-stub (HeroDetailServiceSpy)_](#spy-stub)
|
||||
* [The override tests](#override-tests)
|
||||
* [More overrides](#more-overrides)
|
||||
|
||||
* [Test a _RouterOutlet_ component](#router-outlet-component)
|
||||
- [Stubbing unneeded components](#stub-component)
|
||||
- [Stubbing the _RouterLink_](#router-link-stub)
|
||||
- [_By.directive_ and injected directives](#by-directive)
|
||||
- [What good are these tests?](#why-stubbed-routerlink-tests)
|
||||
|
||||
* [Stubbing unneeded components](#stub-component)
|
||||
* [Stubbing the _RouterLink_](#router-link-stub)
|
||||
* [_By.directive_ and injected directives](#by-directive)
|
||||
* [What good are these tests?](#why-stubbed-routerlink-tests)
|
||||
|
||||
* ["Shallow component tests" with *NO\_ERRORS\_SCHEMA*](#shallow-component-test)
|
||||
<br><br>
|
||||
* [Test an attribute directive](#attribute-directive)
|
||||
<br><br>
|
||||
* [Isolated unit tests](#isolated-unit-tests "Unit testing without the Angular testing utilities")
|
||||
- [Services](#isolated-service-tests)
|
||||
- [Services with dependencies](#services-with-dependencies)
|
||||
- [Pipes](#isolated-pipe-tests)
|
||||
- [Write Angular tests too](#write-tests)
|
||||
- [Components](#isolated-component-tests)
|
||||
|
||||
* [Services](#isolated-service-tests)
|
||||
* [Services with dependencies](#services-with-dependencies)
|
||||
* [Pipes](#isolated-pipe-tests)
|
||||
* [Write Angular tests too](#write-tests)
|
||||
* [Components](#isolated-component-tests)
|
||||
|
||||
* [Angular testing utility APIs](#atu-apis)
|
||||
- [_TestBed_ class summary](#testbed-class-summary)
|
||||
- [The _ComponentFixture_](#component-fixture-api-summary)
|
||||
- [_ComponentFixture_ properties](#component-fixture-properties)
|
||||
- [The _ComponentFixture_ methods](#component-fixture-methods)
|
||||
- [_DebugElement_](#debug-element-details)
|
||||
|
||||
* [_TestBed_ class summary](#testbed-class-summary)
|
||||
* [The _ComponentFixture_](#component-fixture-api-summary)
|
||||
* [_ComponentFixture_ properties](#component-fixture-properties)
|
||||
* [The _ComponentFixture_ methods](#component-fixture-methods)
|
||||
* [_DebugElement_](#debug-element-details)
|
||||
|
||||
* [Test environment setup files](#setup-files)
|
||||
- [npm packages](#npm-packages)
|
||||
|
||||
* [npm packages](#npm-packages)
|
||||
|
||||
* [FAQ: Frequently asked questions](#faq "Frequently asked questions")
|
||||
:marked
|
||||
It’s a big agenda. Fortunately, you can learn a little bit at a time and put each lesson to use.
|
||||
|
@ -927,9 +953,10 @@ a#component-with-input-output
|
|||
:marked
|
||||
While testing a component this simple has little intrinsic value, it's worth knowing how.
|
||||
You can use one of these approaches:
|
||||
- Test it as used by `DashboardComponent`.
|
||||
- Test it as a stand-alone component.
|
||||
- Test it as used by a substitute for `DashboardComponent`.
|
||||
|
||||
* Test it as used by `DashboardComponent`.
|
||||
* Test it as a stand-alone component.
|
||||
* Test it as used by a substitute for `DashboardComponent`.
|
||||
|
||||
A quick look at the `DashboardComponent` constructor discourages the first approach:
|
||||
+makeExample('testing/ts/src/app/dashboard/dashboard.component.ts', 'ctor', 'src/app/dashboard/dashboard.component.ts (constructor)')(format='.')
|
||||
|
@ -1062,6 +1089,7 @@ a#component-inside-test-host
|
|||
+makeExample('testing/ts/src/app/dashboard/dashboard-hero.component.spec.ts', 'test-host-setup', 'src/app/dashboard/dashboard-hero.component.spec.ts (test host setup)')(format='.')
|
||||
:marked
|
||||
This testing module configuration shows two important differences:
|
||||
|
||||
1. It _declares_ both the `DashboardHeroComponent` and the `TestHostComponent`.
|
||||
1. It _creates_ the `TestHostComponent` instead of the `DashboardHeroComponent`.
|
||||
|
||||
|
@ -1122,6 +1150,7 @@ a#inject
|
|||
It injects services into the test function where you can alter, spy on, and manipulate them.
|
||||
|
||||
The `inject` function has two parameters:
|
||||
|
||||
1. An array of Angular dependency injection tokens.
|
||||
1. A test function whose parameters correspond exactly to each item in the injection token array.
|
||||
|
||||
|
@ -1259,6 +1288,7 @@ figure.image-display
|
|||
+makeExample('testing/ts/src/app/hero/hero-detail.component.html', '', 'src/app/hero/hero-detail.component.html')(format='.')
|
||||
:marked
|
||||
To fully exercise the component, the test needs a lot of setup:
|
||||
|
||||
* It must wait until a hero arrives before `*ngIf` allows any element in DOM.
|
||||
* It needs references to the title `<span>` and the name `<input>` so it can inspect their values.
|
||||
* It needs references to the two buttons so it can click them.
|
||||
|
@ -1304,6 +1334,7 @@ a#import-module
|
|||
|
||||
The `HeroDetailComponent` requires a lot of help despite its small size and simple construction.
|
||||
In addition to the support it receives from the default testing module `CommonModule`, it needs:
|
||||
|
||||
* `NgModel` and friends in the `FormsModule` to enable two-way data binding.
|
||||
* The `TitleCasePipe` from the `shared` folder.
|
||||
* Router services (which these tests are stubbing).
|
||||
|
@ -1651,6 +1682,7 @@ figure.image-display
|
|||
`By.css('*:not([highlight])')` finds _any_ element that does not have the directive.
|
||||
|
||||
// Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
|
||||
|
||||
* `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction.
|
||||
But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction.
|
||||
|
||||
|
@ -1660,6 +1692,7 @@ figure.image-display
|
|||
and its `defaultColor`.
|
||||
|
||||
// Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
|
||||
|
||||
* `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
|
||||
|
||||
a(href="#top").to-top Back to top
|
||||
|
@ -1677,12 +1710,14 @@ a#isolated-unit-tests
|
|||
Such tests are often smaller and easier to read, write, and maintain.
|
||||
|
||||
They don't carry extra baggage:
|
||||
|
||||
* Import from the Angular test libraries.
|
||||
* Configure a module.
|
||||
* Prepare dependency injection `providers`.
|
||||
* Call `inject` or `async` or `fakeAsync`.
|
||||
|
||||
They follow patterns familiar to test developers everywhere:
|
||||
|
||||
* Exhibit standard, Angular-agnostic testing techniques.
|
||||
* Create instances directly with `new`.
|
||||
* Substitute test doubles (stubs, spys, and mocks) for the real dependencies.
|
||||
|
@ -2293,6 +2328,7 @@ a#query-predicate
|
|||
|
||||
:marked
|
||||
The Angular `By` class has three static methods for common predicates:
|
||||
|
||||
* `By.all` - return all elements.
|
||||
* `By.css(selector)` - return elements with matching CSS selectors.
|
||||
* `By.directive(directive)` - return elements that Angular matched to an instance of the directive class.
|
||||
|
@ -2351,6 +2387,7 @@ a(href="#top").to-top Back to top
|
|||
a#setup-files
|
||||
:marked
|
||||
## Test environment setup files
|
||||
|
||||
Unit testing requires some configuration and bootstrapping that is captured in _setup files_.
|
||||
The setup files for this guide are provided for you when you follow the [Setup](setup.html) instructions.
|
||||
The CLI delivers similar files with the same purpose.
|
||||
|
@ -2429,6 +2466,7 @@ a(href="#top").to-top Back to top
|
|||
//
|
||||
:marked
|
||||
General
|
||||
|
||||
* [When are end-to-end (e2e) tests a good choice?](#q-when-e2e)
|
||||
* [When to use the _TestBed_?](#q-why-testbed)
|
||||
* [When to write isolated unit tests without the _TestBed_?](#q-when-no-testbed)
|
||||
|
@ -2451,6 +2489,7 @@ a(href="#top").to-top Back to top
|
|||
* [How do I use the Jasmine HTML TestRunner in the browser?](#q-jasmine-browser-test-runner)
|
||||
|
||||
Resources
|
||||
|
||||
* [Where can I learn more about unit testing in JavaScript?](#q-js-unit-testing-resources)
|
||||
* [Where can I learn more about testing with Jasmine?](#q-jasmine-resources)
|
||||
* [Where can I learn more about testing with karma?](#q-karma-resources)
|
||||
|
@ -2466,11 +2505,12 @@ a(href="#top").to-top Back to top
|
|||
|
||||
It's a good idea to put unit test spec files in the same folder
|
||||
as the application source code files that they test:
|
||||
- Such tests are easy to find.
|
||||
- You see at a glance if a part of your application lacks tests.
|
||||
- Nearby tests can reveal how a part works in context.
|
||||
- When you move the source (inevitable), you remember to move the test.
|
||||
- When you rename the source file (inevitable), you remember to rename the test file.
|
||||
|
||||
* Such tests are easy to find.
|
||||
* You see at a glance if a part of your application lacks tests.
|
||||
* Nearby tests can reveal how a part works in context.
|
||||
* When you move the source (inevitable), you remember to move the test.
|
||||
* When you rename the source file (inevitable), you remember to rename the test file.
|
||||
|
||||
.l-hr
|
||||
|
||||
|
|
|
@ -6,39 +6,59 @@
|
|||
## Table of Contents
|
||||
|
||||
* [Overview](#overview)
|
||||
|
||||
* [How does it work?](#how-does-it-work)
|
||||
* [Why do it?](#why-do-it)
|
||||
|
||||
* [SEO / No JavaScript](#seo-no-javascript)
|
||||
* [Startup Performance](#startup-performance)
|
||||
|
||||
* [The Example](#the-example)
|
||||
|
||||
* [Preparation](#preparation)
|
||||
|
||||
* [Installing the tools](#installing-the-tools)
|
||||
* [Component-relative URLs](#component-relative-urls)
|
||||
* [Server Transition](#server-transition)
|
||||
|
||||
* [Configuration - AOT](#configuration-aot)
|
||||
|
||||
* [Main Entry Point](#main-entry-point)
|
||||
* [Creating the tsconfig-aot.json](#creating-the-tsconfig-aot-json)
|
||||
* [Webpack Configuration](#webpack-configuration)
|
||||
|
||||
* [Loader](#loader)
|
||||
* [Plugin](#plugin)
|
||||
* [Input](#input)
|
||||
* [Output](#output)
|
||||
|
||||
* [Build - AOT](#build-aot)
|
||||
|
||||
* [Source Maps](#source-maps)
|
||||
|
||||
* [Serve - AOT](#serve-aot)
|
||||
|
||||
* [Lite Server Configuration](#lite-server-configuration)
|
||||
* [Serve Command](#serve-command)
|
||||
|
||||
* [Configuration - Universal](#configuration-universal)
|
||||
|
||||
* [Server Code](#server-code)
|
||||
|
||||
* [App Server Module](#app-server-module)
|
||||
* [Universal Engine](#universal-engine)
|
||||
* [Web Server](#web-server)
|
||||
|
||||
* [Creating the tsconfig-uni.json](#creating-the-tsconfig-uni-json)
|
||||
* [Creating the webpack.config.uni.js](#creating-the-webpack-config-uni-js)
|
||||
|
||||
* [The entry points](#the-entry-points)
|
||||
* [The output file](#the-output-file)
|
||||
|
||||
* [Build and Serve - Universal](#build-and-serve-universal)
|
||||
|
||||
* [Exercising Universal](#exercising-universal)
|
||||
|
||||
* [Disabling the Client App](#disabling-the-client-app)
|
||||
* [Throttling](#throttling)
|
||||
* [Conclusion](#conclusion)
|
||||
|
@ -158,19 +178,19 @@ The AOT and Universal versions of the app are both compiled by the AOT compiler.
|
|||
|
||||
To build and run the AOT version, you need to create:
|
||||
|
||||
- an `index-aot.html` file
|
||||
- a main entry point, `main-aot.ts`
|
||||
- a TypeScript config file, `tsconfig-aot.json`
|
||||
- a Webpack config file, `webpack.config.aot.js`
|
||||
- a lite-server config file, `bs-config.aot.js`
|
||||
* an `index-aot.html` file
|
||||
* a main entry point, `main-aot.ts`
|
||||
* a TypeScript config file, `tsconfig-aot.json`
|
||||
* a Webpack config file, `webpack.config.aot.js`
|
||||
* a lite-server config file, `bs-config.aot.js`
|
||||
|
||||
To build and run the Universal version, you need to create:
|
||||
|
||||
- a server-side app module, `app.server.ts`
|
||||
- a Universal app renderer, `universal-engine.ts`
|
||||
- an express web server to handle requests, `server-aot.ts`
|
||||
- a TypeScript config file, `tsconfig-uni.json`
|
||||
- a Webpack config file, `webpack.config.uni.js`
|
||||
* a server-side app module, `app.server.ts`
|
||||
* a Universal app renderer, `universal-engine.ts`
|
||||
* an express web server to handle requests, `server-aot.ts`
|
||||
* a TypeScript config file, `tsconfig-uni.json`
|
||||
* a Webpack config file, `webpack.config.uni.js`
|
||||
|
||||
The folder structure will look like this:
|
||||
|
||||
|
@ -206,12 +226,12 @@ The files marked with * are new and not in the original Tour of Heroes demo. Th
|
|||
|
||||
To get started, you need to install the necessary modules for AOT and Webpack.
|
||||
|
||||
- `@angular/compiler-cli` - The ngc compiler that compiles Angular applications
|
||||
- `@angular/platform-server` - Server-side components needed for compilation
|
||||
- `webpack` - The Webpack JavaScript bundler
|
||||
- `@ngtools/webpack` - The Webpack loader and plugin for bundling compiled applications
|
||||
- `raw-loader` - The Webpack loader for text files
|
||||
- `express` - The web server for serving the Universal application
|
||||
* `@angular/compiler-cli` - The ngc compiler that compiles Angular applications
|
||||
* `@angular/platform-server` - Server-side components needed for compilation
|
||||
* `webpack` - The Webpack JavaScript bundler
|
||||
* `@ngtools/webpack` - The Webpack loader and plugin for bundling compiled applications
|
||||
* `raw-loader` - The Webpack loader for text files
|
||||
* `express` - The web server for serving the Universal application
|
||||
|
||||
You can install them with the following commands:
|
||||
|
||||
|
@ -306,13 +326,14 @@ Since `app.module.ts` appears first in the array, it will be compiled into an `n
|
|||
The AOT compiler transpiles TypeScript into JavaScript (like `tsc`), and compiles your app's components, services, etc. into executable JavaScript code.
|
||||
You configure it using a JSON file similar to `tsconfig.json`. There are a few differences:
|
||||
|
||||
- The `module` setting must be `es2015`.
|
||||
* The `module` setting must be `es2015`.
|
||||
This creates JavaScript output with `import` statements (instead of `require()`) that can be compiled and bundled.
|
||||
- The `files` setting includes the app module and the main AOT bootstrapper. See more about this in the section below.
|
||||
- There is a new `angularCompilerOptions` section with the following settings:
|
||||
- `genDir` - the output directory that will contain the compiled `ngfactory` code. When compiling via Webpack, this is used as a temporary directory.
|
||||
- `entryModule` - the root module of the app, expressed as **path/to/file#ClassName**.
|
||||
- `skipMetadataEmit` - set to `true` because you don't need metadata in the bundled application
|
||||
* The `files` setting includes the app module and the main AOT bootstrapper. See more about this in the section below.
|
||||
* There is a new `angularCompilerOptions` section with the following settings:
|
||||
|
||||
* `genDir` - the output directory that will contain the compiled `ngfactory` code. When compiling via Webpack, this is used as a temporary directory.
|
||||
* `entryModule` - the root module of the app, expressed as **path/to/file#ClassName**.
|
||||
* `skipMetadataEmit` - set to `true` because you don't need metadata in the bundled application
|
||||
|
||||
Create a `tsconfig-aot.json` file in the project rood directory by copying your `tsconfig.json` and applying the changes described above. It should look like this:
|
||||
|
||||
|
@ -809,10 +830,3 @@ Now rename `build.tmp.js` back to `build.js` so the app can load again. Then op
|
|||
# Conclusion <a name="conclusion"></a>
|
||||
|
||||
Angular Universal can greatly improve the perceived startup performance of your app. The slower the network, the more advantageous it becomes to have Universal display the first page to the user.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -27,11 +27,14 @@ include ../_util-fns
|
|||
make incremental upgrading seamless.
|
||||
|
||||
1. [Preparation](#preparation)
|
||||
|
||||
1. [Follow the Angular Style Guide](#follow-the-angular-style-guide)
|
||||
2. [Using a Module Loader](#using-a-module-loader)
|
||||
3. [Migrating to TypeScript](#migrating-to-typescript)
|
||||
4. [Using Component Directives](#using-component-directives)
|
||||
|
||||
2. [Upgrading with The Upgrade Module](#upgrading-with-the-upgrade-module)
|
||||
|
||||
1. [How The Upgrade Module Works](#how-the-upgrade-module-works)
|
||||
2. [Bootstrapping hybrid](#bootstrapping-hybrid-applications)
|
||||
3. [Using Angular Components from AngularJS Code](#using-angular-components-from-angularjs-code)
|
||||
|
@ -42,7 +45,9 @@ include ../_util-fns
|
|||
8. [Making Angular Dependencies Injectable to AngularJS](#making-angular-dependencies-injectable-to-angularjs)
|
||||
9. [Using Ahead-of-time compilation with hybrid apps](#using-ahead-of-time-compilation-with-hybrid-apps)
|
||||
10. [Dividing routes between Angular and AngularJS](#dividing-routes-between-angular-and-angularjs)
|
||||
|
||||
3. [PhoneCat Upgrade Tutorial](#phonecat-upgrade-tutorial)
|
||||
|
||||
1. [Switching to TypeScript](#switching-to-typescript)
|
||||
2. [Installing Angular](#installing-angular)
|
||||
3. [Bootstrapping a hybrid PhoneCat](#bootstrapping-a-hybrid-phonecat)
|
||||
|
@ -51,6 +56,7 @@ include ../_util-fns
|
|||
6. [AoT compile the hybrid app](#aot-compile-the-hybrid-app)
|
||||
7. [Adding The Angular Router And Bootstrap](#adding-the-angular-router-and-bootstrap)
|
||||
8. [Say Goodbye to AngularJS](#say-goodbye-to-angularjs)
|
||||
|
||||
3. [Appendix: Upgrading PhoneCat Tests](#appendix-upgrading-phonecat-tests)
|
||||
|
||||
.l-main-section
|
||||
|
@ -79,6 +85,7 @@ include ../_util-fns
|
|||
aligned with Angular*.
|
||||
|
||||
There are a few rules in particular that will make it much easier to do
|
||||
|
||||
*an incremental upgrade* using the Angular `upgrade` module:
|
||||
|
||||
* The [Rule of 1](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#single-responsibility)
|
||||
|
@ -270,11 +277,12 @@ table
|
|||
interoperability. The `UpgradeModule` resolves the differences and makes
|
||||
everything work seamlessly:
|
||||
|
||||
* We can make AngularJS services available for injection to Angular code
|
||||
* You can make AngularJS services available for injection to Angular code
|
||||
by *upgrading* them. The same singleton instance of each service is shared
|
||||
between the frameworks. In Angular these services will always be in the
|
||||
*root injector* and available to all components.
|
||||
* We can also make Angular services available for injection to AngularJS code
|
||||
|
||||
* You can also make Angular services available for injection to AngularJS code
|
||||
by *downgrading* them. Only services from the Angular root injector can
|
||||
be downgraded. Again, the same singleton instances are shared between the frameworks.
|
||||
When we register a downgrade, we explicitly specify a *string token* that we want to
|
||||
|
@ -300,6 +308,7 @@ figure.image-display
|
|||
frameworks. The other framework ignores it. If an element is
|
||||
owned by AngularJS, Angular treats it as if it didn't exist,
|
||||
and vice versa.
|
||||
|
||||
2. The root of the application *is always an AngularJS template*.
|
||||
|
||||
So a hybrid application begins life as an AngularJS application,
|
||||
|
@ -316,6 +325,7 @@ figure.image-display
|
|||
1. By using a component from the other framework: An AngularJS template
|
||||
using an Angular component, or an Angular template using an
|
||||
AngularJS component.
|
||||
|
||||
2. By transcluding or projecting content from the other framework. The
|
||||
`UpgradeModule` bridges the related concepts of AngularJS transclusion
|
||||
and Angular content projection together.
|
||||
|
|
|
@ -17,24 +17,30 @@ style.
|
|||
# Contents
|
||||
|
||||
* [What is Webpack?](#what-is-webpack)
|
||||
|
||||
* [Entries and outputs](#entries-outputs)
|
||||
* [Multiple bundles](#multiple-bundles)
|
||||
* [Loaders](#loaders)
|
||||
* [Plugins](#plugins)
|
||||
|
||||
* [Configuring Webpack](#configure-webpack)
|
||||
|
||||
* [Polyfills](#polyfills)
|
||||
* [Common configuration](#common-configuration)
|
||||
* [Inside `webpack.common.js`](#inside-webpack-commonjs)
|
||||
- [entry](#common-entries)
|
||||
- [resolve extension-less imports](#common-resolves)
|
||||
- [`module.rules`](#common-rules)
|
||||
- [Plugins](#plugins)
|
||||
- [`CommonsChunkPlugin`](#commons-chunk-plugin)
|
||||
- [`HtmlWebpackPlugin`](#html-webpack-plugin)
|
||||
|
||||
* [entry](#common-entries)
|
||||
* [resolve extension-less imports](#common-resolves)
|
||||
* [`module.rules`](#common-rules)
|
||||
* [Plugins](#plugins)
|
||||
* [`CommonsChunkPlugin`](#commons-chunk-plugin)
|
||||
* [`HtmlWebpackPlugin`](#html-webpack-plugin)
|
||||
|
||||
* [Environment specific configuration](#environment-configuration)
|
||||
* [Development configuration](#development-configuration)
|
||||
* [Production configuration](#production-configuration)
|
||||
* [Test configuration](#test-configuration)
|
||||
|
||||
* [Trying it out](#try)
|
||||
* [Highlights](#highlights)
|
||||
* [Conclusion](#conclusion)
|
||||
|
|
|
@ -155,6 +155,7 @@ code-example(language="sh" class="code-shell").
|
|||
.l-main-section
|
||||
:marked
|
||||
## The road you've travelled
|
||||
|
||||
Take stock of what you've built.
|
||||
|
||||
* The Tour of Heroes app uses the double curly braces of interpolation (a type of one-way data binding)
|
||||
|
|
|
@ -276,6 +276,7 @@ code-example(language="sh" class="code-shell").
|
|||
.l-main-section
|
||||
:marked
|
||||
## The road you've travelled
|
||||
|
||||
Here's what you achieved in this page:
|
||||
|
||||
* The Tour of Heroes app displays a list of selectable heroes.
|
||||
|
|
|
@ -243,6 +243,7 @@ a#add-hero-detail
|
|||
.l-main-section
|
||||
:marked
|
||||
## The road you’ve travelled
|
||||
|
||||
Here's what you achieved in this page:
|
||||
|
||||
* You created a reusable component.
|
||||
|
|
|
@ -152,6 +152,7 @@ code-example(language="sh" class="code-shell").
|
|||
### Inject the *HeroService*
|
||||
|
||||
Instead of using the *new* line, you'll add two lines.
|
||||
|
||||
* Add a constructor that also defines a private property.
|
||||
* Add to the component's `providers` metadata.
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ include ../../../_includes/_see-addr-bar
|
|||
and create a separate `AppComponent` shell.
|
||||
|
||||
Do the following:
|
||||
|
||||
* Rename the <code>app.component.ts</code> file to <code>heroes.component.ts</code>.
|
||||
* Rename the `AppComponent` class as `HeroesComponent` (rename locally, _only_ in this file).
|
||||
* Rename the selector `my-app` as `my-heroes`.
|
||||
|
@ -118,8 +119,10 @@ include ../../../_includes/_see-addr-bar
|
|||
* Define an exported `AppComponent` class.
|
||||
* Add an `@Component` decorator above the class with a `my-app` selector.
|
||||
* Move the following from `HeroesComponent` to `AppComponent`:
|
||||
|
||||
* `title` class property.
|
||||
* `@Component` template `<h1>` element, which contains a binding to `title`.
|
||||
|
||||
* Add a `<my-heroes>` element to the app template just below the heading so you still see the heroes.
|
||||
* Add `HeroesComponent` to the `declarations` array of `AppModule` so Angular recognizes the `<my-heroes>` tags.
|
||||
* Add `HeroService` to the `providers` array of `AppModule` because you'll need it in every other view.
|
||||
|
@ -187,8 +190,8 @@ a#configure-routes
|
|||
|
||||
This route definition has the following parts:
|
||||
|
||||
- *Path*: The router matches this route's path to the URL in the browser address bar (`heroes`).
|
||||
- *Component*: The component that the router should create when navigating to this route (`HeroesComponent`).
|
||||
* *Path*: The router matches this route's path to the URL in the browser address bar (`heroes`).
|
||||
* *Component*: The component that the router should create when navigating to this route (`HeroesComponent`).
|
||||
|
||||
|
||||
.l-sub-section
|
||||
|
@ -603,6 +606,7 @@ code-example(format="nocode").
|
|||
+makeExample('toh-5/ts/src/app/app-routing.module.ts', null, 'src/app/app-routing.module.ts')
|
||||
:marked
|
||||
The following points are typical of routing modules:
|
||||
|
||||
* The Routing Module pulls the routes into a variable. The variable clarifies the
|
||||
routing module pattern in case you export the module in the future.
|
||||
* The Routing Module adds `RouterModule.forRoot(routes)` to `imports`.
|
||||
|
@ -878,12 +882,12 @@ figure.image-display
|
|||
## The road you’ve travelled
|
||||
Here's what you achieved in this page:
|
||||
|
||||
- You added the Angular router to navigate among different components.
|
||||
- You learned how to create router links to represent navigation menu items.
|
||||
- You used router link parameters to navigate to the details of the user-selected hero.
|
||||
- You shared the `HeroService` among multiple components.
|
||||
- You moved HTML and CSS out of the component file and into their own files.
|
||||
- You added the `uppercase` pipe to format data.
|
||||
* You added the Angular router to navigate among different components.
|
||||
* You learned how to create router links to represent navigation menu items.
|
||||
* You used router link parameters to navigate to the details of the user-selected hero.
|
||||
* You shared the `HeroService` among multiple components.
|
||||
* You moved HTML and CSS out of the component file and into their own files.
|
||||
* You added the `uppercase` pipe to format data.
|
||||
|
||||
Your app should look like this <live-example></live-example>.
|
||||
|
||||
|
|
|
@ -561,12 +561,12 @@ figure.image-display
|
|||
## Home Stretch
|
||||
|
||||
You're at the end of your journey, and you've accomplished a lot.
|
||||
- You added the necessary dependencies to use HTTP in the app.
|
||||
- You refactored `HeroService` to load heroes from a web API.
|
||||
- You extended `HeroService` to support `post()`, `put()`, and `delete()` methods.
|
||||
- You updated the components to allow adding, editing, and deleting of heroes.
|
||||
- You configured an in-memory web API.
|
||||
- You learned how to use Observables.
|
||||
* You added the necessary dependencies to use HTTP in the app.
|
||||
* You refactored `HeroService` to load heroes from a web API.
|
||||
* You extended `HeroService` to support `post()`, `put()`, and `delete()` methods.
|
||||
* You updated the components to allow adding, editing, and deleting of heroes.
|
||||
* You configured an in-memory web API.
|
||||
* You learned how to use Observables.
|
||||
|
||||
Here are the files you added or changed in this page.
|
||||
|
||||
|
|
Loading…
Reference in New Issue