docs(*): blank lines before lists are good (#3474)

This commit is contained in:
Pete Bacon Darwin 2017-03-30 22:41:23 +01:00 committed by Ward Bell
parent 22918b40b5
commit 93d3db1fd4
11 changed files with 593 additions and 576 deletions

View File

@ -7,6 +7,7 @@ include ../_util-fns
a#toc a#toc
:marked :marked
# Contents # Contents
- [Overview](overview) - [Overview](overview)
- [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit) - [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit)
- [Why do AOT compilation?](#why-aot) - [Why do AOT compilation?](#why-aot)
@ -282,8 +283,8 @@ a#rollup-plugins
Luckily, there is a Rollup plugin that modifies _RxJs_ Luckily, there is a Rollup plugin that modifies _RxJs_
to use the ES `import` and `export` statements that Rollup requires. to use the ES `import` and `export` statements that Rollup requires.
Rollup then preserves the parts of `RxJS` referenced by the application Rollup then preserves the parts of `RxJS` referenced by the application
in the final bundle. Using it is straigthforward. Add the following to in the final bundle. Using it is straigthforward. Add the following to
the `plugins` !{_array} in `rollup-config.js`: the `plugins` !{_array} in `rollup-config.js`:
+makeExample('cb-aot-compiler/ts/rollup-config.js','commonjs','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js','commonjs','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.')
@ -292,7 +293,7 @@ a#rollup-plugins
*Minification* *Minification*
Rollup tree shaking reduces code size considerably. Minification makes it smaller still. Rollup tree shaking reduces code size considerably. Minification makes it smaller still.
This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code. This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code.
Add the following to the `plugins` !{_array}: Add the following to the `plugins` !{_array}:
+makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.')
@ -398,14 +399,14 @@ code-example(language="none" class="code-shell").
:marked :marked
That compiles the app with JIT and launches the server. That compiles the app with JIT and launches the server.
The server loads `index.html` which is still the AOT version, which you can confirm in the browser console. The server loads `index.html` which is still the AOT version, which you can confirm in the browser console.
Change the address bar to `index-jit.html` and it loads the JIT version. Change the address bar to `index-jit.html` and it loads the JIT version.
This is also evident in the browser console. This is also evident in the browser console.
Develop as usual. Develop as usual.
The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser. The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser.
To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`. To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`.
When it finishes, go back to the browser and use the back button to When it finishes, go back to the browser and use the back button to
return to the AOT version in the default `index.html`. return to the AOT version in the default `index.html`.
Now you can develop JIT and AOT, side-by-side. Now you can develop JIT and AOT, side-by-side.

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,11 @@ a#top
:marked :marked
Improve overall data quality by validating user input for accuracy and completeness. Improve overall data quality by validating user input for accuracy and completeness.
This cookbook shows how to validate user input in the UI and display useful validation messages This cookbook shows how to validate user input in the UI and display useful validation messages
using first the template-driven forms and then the reactive forms approach. using first the template-driven forms and then the reactive forms approach.
.l-sub-section .l-sub-section
:marked :marked
Read more about these choices in the [Forms](../guide/forms.html) Read more about these choices in the [Forms](../guide/forms.html)
and the [Reactive Forms](../guide/reactive-forms.html) guides. and the [Reactive Forms](../guide/reactive-forms.html) guides.
a#toc a#toc
@ -40,20 +40,20 @@ a#template1
:marked :marked
## Simple template-driven forms ## Simple template-driven forms
In the template-driven approach, you arrange In the template-driven approach, you arrange
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template. [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
You add Angular form directives (mostly directives beginning `ng...`) to help You add Angular form directives (mostly directives beginning `ng...`) to help
Angular construct a corresponding internal control model that implements form functionality. Angular construct a corresponding internal control model that implements form functionality.
In template-drive forms, the control model is _implicit_ in the template. In template-drive forms, the control model is _implicit_ in the template.
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
to the elements. Angular interprets those as well, adding validator functions to the control model. to the elements. Angular interprets those as well, adding validator functions to the control model.
Angular exposes information about the state of the controls including Angular exposes information about the state of the controls including
whether the user has "touched" the control or made changes and if the control values are valid. whether the user has "touched" the control or made changes and if the control values are valid.
In this first template validation example, In this first template validation example,
notice the HTML that reads the control state and updates the display appropriately. notice the HTML that reads the control state and updates the display appropriately.
Here's an excerpt from the template HTML for a single input control bound to the hero name: Here's an excerpt from the template HTML for a single input control bound to the hero name:
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','name-with-error-msg','template/hero-form-template1.component.html (Hero name)')(format='.') +makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','name-with-error-msg','template/hero-form-template1.component.html (Hero name)')(format='.')
@ -66,9 +66,9 @@ a#template1
with an Angular form control called `name` in its internal control model. with an Angular form control called `name` in its internal control model.
- The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property. - The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
- The template variable (`#name`) has the value `"ngModel"` (always `ngModel`). - The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
This gives you a reference to the Angular `NgModel` directive This gives you a reference to the Angular `NgModel` directive
associated with this control that you can use _in the template_ associated with this control that you can use _in the template_
to check for control states such as `valid` and `dirty`. to check for control states such as `valid` and `dirty`.
@ -102,7 +102,7 @@ a#why-check
+makeTabs( +makeTabs(
`cb-form-validation/ts/src/app/template/hero-form-template1.component.html, `cb-form-validation/ts/src/app/template/hero-form-template1.component.html,
cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`, cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`,
'', '',
`template/hero-form-template1.component.html, `template/hero-form-template1.component.html,
template/hero-form-template1.component.ts`) template/hero-form-template1.component.ts`)
@ -111,24 +111,24 @@ a#template2
:marked :marked
## Template-driven forms with validation messages in code ## Template-driven forms with validation messages in code
While the layout is straightforward, While the layout is straightforward,
there are obvious shortcomings with the way it's handling validation messages: there are obvious shortcomings with the way it's handling validation messages:
* It takes a lot of HTML to represent all possible error conditions. * It takes a lot of HTML to represent all possible error conditions.
This gets out of hand when there are many controls and many validation rules. This gets out of hand when there are many controls and many validation rules.
* There's a lot of JavaScript logic in the HTML. * There's a lot of JavaScript logic in the HTML.
* The messages are static strings, hard-coded into the template. * The messages are static strings, hard-coded into the template.
It's easier to maintain _dynamic_ messages in the component class. It's easier to maintain _dynamic_ messages in the component class.
In this example, you can move the logic and the messages into the component with a few changes to In this example, you can move the logic and the messages into the component with a few changes to
the template and component. the template and component.
Here's the hero name again, excerpted from the revised template Here's the hero name again, excerpted from the revised template
(Template 2), next to the original version: (Template 2), next to the original version:
+makeTabs( +makeTabs(
`cb-form-validation/ts/src/app/template/hero-form-template2.component.html, `cb-form-validation/ts/src/app/template/hero-form-template2.component.html,
cb-form-validation/ts/src/app/template/hero-form-template1.component.html`, cb-form-validation/ts/src/app/template/hero-form-template1.component.html`,
'name-with-error-msg, name-with-error-msg', 'name-with-error-msg, name-with-error-msg',
`hero-form-template2.component.html (name #2), `hero-form-template2.component.html (name #2),
@ -140,24 +140,24 @@ a#template2
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive. - There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
It invalidates the control if the user enters "bob" in the name `<input>`([try it](#live-example)). 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 See the [custom validation](#custom-validation) section later in this cookbook for more information
on custom validation directives. on custom validation directives.
- The `#name` template variable is gone because the app no longer refers to the Angular control for this element. - The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
- 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 a#component-class
:marked :marked
### Component class ### Component class
The original component code for Template 1 stayed the same; however, The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code Template 2 requires some changes in the component. This section covers the code
necessary in Template 2's component class to acquire the Angular necessary in Template 2's component class to acquire the Angular
form control and compose error messages. form control and compose error messages.
The first step is to acquire the form control that Angular created from the template by querying for it. The first step is to acquire the form control that Angular created from the template by querying for it.
Look back at the top of the component template at the Look back at the top of the component template at the
`#heroForm` template variable in the `<form>` element: `#heroForm` template variable in the `<form>` element:
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','form-tag','template/hero-form-template1.component.html (form tag)')(format='.') +makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','form-tag','template/hero-form-template1.component.html (form tag)')(format='.')
@ -169,22 +169,22 @@ a#component-class
:marked :marked
Some observations: Some observations:
- Angular `@ViewChild` queries for a template variable when you pass it - Angular `@ViewChild` queries for a template variable when you pass it
the name of that variable as a string (`'heroForm'` in this case). the name of that variable as a string (`'heroForm'` in this case).
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero. - The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes. Periodically inspecting it reveals these changes.
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview) - Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
when anything changes in the view. when anything changes in the view.
That's the right time to see if there's a new `heroForm` object. That's the right time to see if there's a new `heroForm` object.
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property. - When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
The `onValueChanged` handler looks for validation errors after every keystroke. The `onValueChanged` handler looks for validation errors after every keystroke.
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.') +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.')
:marked :marked
The `onValueChanged` handler interprets user data entry. The `onValueChanged` handler interprets user data entry.
The `data` object passed into the handler contains the current element values. The `data` object passed into the handler contains the current element values.
The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object. The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object.
@ -194,11 +194,11 @@ a#component-class
For each field, the `onValueChanged` handler does the following: For each field, the `onValueChanged` handler does the following:
- Clears the prior error message, if any. - Clears the prior error message, if any.
- Acquires the field's corresponding Angular form control. - Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty") - If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors. _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
Next, the component needs some error messages of course&mdash;a set for each validated property with Next, the component needs some error messages of course&mdash;a set for each validated property with
one message per validation rule: one message per validation rule:
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.') +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.')
:marked :marked
@ -207,25 +207,25 @@ a#component-class
a#improvement a#improvement
:marked :marked
### The benefits of messages in code ### The benefits of messages in code
Clearly the template got substantially smaller while the component code got substantially larger. Clearly the template got substantially smaller while the component code got substantially larger.
It's not easy to see the benefit when there are just three fields and only two of them have validation rules. It's not easy to see the benefit when there are just three fields and only two of them have validation rules.
Consider what happens as the number of validated Consider what happens as the number of validated
fields and rules increases. fields and rules increases.
In general, HTML is harder to read and maintain than code. In general, HTML is harder to read and maintain than code.
The initial template was already large and threatening to get rapidly worse The initial template was already large and threatening to get rapidly worse
with the addition of more validation message `<div>` elements. with the addition of more validation message `<div>` elements.
After moving the validation messaging to the component, After moving the validation messaging to the component,
the template grows more slowly and proportionally. the template grows more slowly and proportionally.
Each field has approximately the same number of lines no matter its number of validation rules. Each field has approximately the same number of lines no matter its number of validation rules.
The component also grows proportionally, at the rate of one line per validated field The component also grows proportionally, at the rate of one line per validated field
and one line per validation message. and one line per validation message.
Both trends are manageable. Both trends are manageable.
Now that the messages are in code, you have more flexibility and can compose messages more efficiently. Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server. You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code. In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
@ -233,9 +233,9 @@ a#formmodule
:marked :marked
### _FormModule_ and template-driven forms ### _FormModule_ and template-driven forms
Angular has two different forms modules&mdash;`FormsModule` and Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the `ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come two approaches to form development. Both modules come
from the same `@angular/forms` library package. from the same `@angular/forms` library package.
You've been reviewing the "Template-driven" approach which requires the `FormsModule`. You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
@ -245,8 +245,8 @@ a#formmodule
.l-sub-section .l-sub-section
:marked :marked
This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
form template in this cookbook. form template in this cookbook.
They're not germane to the validation story. Look at the [live example](#live-example) if you're interested. They're not germane to the validation story. Look at the [live example](#live-example) if you're interested.
.l-main-section .l-main-section
@ -254,11 +254,11 @@ a#reactive
:marked :marked
## Reactive forms with validation in code ## Reactive forms with validation in code
In the template-driven approach, you markup the template with form elements, validation attributes, In the template-driven approach, you markup the template with form elements, validation attributes,
and `ng...` directives from the Angular `FormsModule`. and `ng...` directives from the Angular `FormsModule`.
At runtime, Angular interprets the template and derives its _form control model_. At runtime, Angular interprets the template and derives its _form control model_.
**Reactive Forms** takes a different approach. **Reactive Forms** takes a different approach.
You create the form control model in code. You write the template with form elements You create the form control model in code. You write the template with form elements
and `form...` directives from the Angular `ReactiveFormsModule`. and `form...` directives from the Angular `ReactiveFormsModule`.
At runtime, Angular binds the template elements to your control model based on your instructions. At runtime, Angular binds the template elements to your control model based on your instructions.
@ -279,7 +279,7 @@ a#reactive-forms-module
The application module for the reactive forms feature in this sample looks like this: The application module for the reactive forms feature in this sample looks like this:
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts','','src/app/reactive/hero-form-reactive.module.ts')(format='.') +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts','','src/app/reactive/hero-form-reactive.module.ts')(format='.')
:marked :marked
The reactive forms feature module and component are in the `src/app/reactive` folder. The reactive forms feature module and component are in the `src/app/reactive` folder.
Focus on the `HeroFormReactiveComponent` there, starting with its template. Focus on the `HeroFormReactiveComponent` there, starting with its template.
a#reactive-component-template a#reactive-component-template
@ -287,7 +287,7 @@ a#reactive-component-template
### Component template ### Component template
Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
to the `heroForm` property in the component class. to the `heroForm` property in the component class.
The `heroForm` is the control model that the component class builds and maintains. The `heroForm` is the control model that the component class builds and maintains.
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html','form-tag')(format='.') +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html','form-tag')(format='.')
@ -295,7 +295,7 @@ a#reactive-component-template
Next, modify the template HTML elements to match the _reactive forms_ style. Next, modify the template HTML elements to match the _reactive forms_ style.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version: Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
+makeTabs( +makeTabs(
`cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html, `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html,
cb-form-validation/ts/src/app/template/hero-form-template2.component.html`, cb-form-validation/ts/src/app/template/hero-form-template2.component.html`,
'name-with-error-msg, name-with-error-msg', 'name-with-error-msg, name-with-error-msg',
`hero-form-reactive.component.html (name #3), `hero-form-reactive.component.html (name #3),
@ -303,16 +303,16 @@ a#reactive-component-template
:marked :marked
Key changes are: Key changes are:
- The validation attributes are gone (except `required`) because - The validation attributes are gone (except `required`) because
validating happens in code. validating happens in code.
- `required` remains, not for validation purposes (that's in the code), - `required` remains, not for validation purposes (that's in the code),
but rather for css styling and accessibility. but rather for css styling and accessibility.
.l-sub-section .l-sub-section
:marked :marked
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
(and perhaps the `aria-required` attribute) when the control has the `required` validator function. (and perhaps the `aria-required` attribute) when the control has the `required` validator function.
Until then, apply the `required` attribute _and_ add the `Validator.required` function Until then, apply the `required` attribute _and_ add the `Validator.required` function
to the control model, as you'll see below. to the control model, as you'll see below.
@ -321,7 +321,7 @@ a#reactive-component-template
- The `formControlName` replaces the `name` attribute; it serves the same - The `formControlName` replaces the `name` attribute; it serves the same
purpose of correlating the input with the Angular form control. purpose of correlating the input with the Angular form control.
- The two-way `[(ngModel)]` binding is gone. - The two-way `[(ngModel)]` binding is gone.
The reactive approach does not use data binding to move data into and out of the form controls. The reactive approach does not use data binding to move data into and out of the form controls.
That's all in code. That's all in code.
@ -332,10 +332,10 @@ a#reactive-component-class
:marked :marked
### Component class ### Component class
The component class is now responsible for defining and managing the form control model. The component class is now responsible for defining and managing the form control model.
Angular no longer derives the control model from the template so you can no longer query for it. Angular no longer derives the control model from the template so you can no longer query for it.
You can create the Angular form control model explicitly with You can create the Angular form control model explicitly with
the help of the `FormBuilder` class. the help of the `FormBuilder` class.
Here's the section of code devoted to that process, paired with the template-driven code it replaces: Here's the section of code devoted to that process, paired with the template-driven code it replaces:
@ -355,35 +355,35 @@ a#reactive-component-class
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook. A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
:marked :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) Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanges` event and calls it immediately to the form's `valueChanges` event and calls it immediately
to set error messages for the new control model. to set error messages for the new control model.
a#formbuilder a#formbuilder
:marked :marked
#### _FormBuilder_ declaration #### _FormBuilder_ declaration
The `FormBuilder` declaration object specifies the three controls of the sample's hero form. The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
Each control spec is a control name with an array value. Each control spec is a control name with an array value.
The first array element is the current value of the corresponding hero field. The first array element is the current value of the corresponding hero field.
The optional second value is a validator function or an array of validator functions. The optional second value is a validator function or an array of validator functions.
Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class. Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class.
Angular has stock validators that correspond to the standard HTML validation attributes. Angular has stock validators that correspond to the standard HTML validation attributes.
The `forbiddenNames` validator on the `"name"` control is a custom validator, The `forbiddenNames` validator on the `"name"` control is a custom validator,
discussed in a separate [section below](#custom-validation). discussed in a separate [section below](#custom-validation).
.l-sub-section .l-sub-section
:marked :marked
Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide. Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide.
a#committing-changes a#committing-changes
:marked :marked
#### Committing hero value changes #### Committing hero value changes
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
Reactive forms do not use data binding to update data model properties. Reactive forms do not use data binding to update data model properties.
The developer decides _when and how_ to update the data model from control values. The developer decides _when and how_ to update the data model from control values.
This sample updates the model twice: This sample updates the model twice:
@ -406,7 +406,7 @@ a#committing-changes
Here's the complete reactive component file, compared to the two template-driven component files. Here's the complete reactive component file, compared to the two template-driven component files.
+makeTabs( +makeTabs(
`cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts, `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts,
cb-form-validation/ts/src/app/template/hero-form-template2.component.ts, cb-form-validation/ts/src/app/template/hero-form-template2.component.ts,
cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`, cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`,
'', '',
`reactive/hero-form-reactive.component.ts (#3), `reactive/hero-form-reactive.component.ts (#3),
@ -422,7 +422,7 @@ a#committing-changes
a#custom-validation a#custom-validation
:marked :marked
## Custom validation ## Custom validation
This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the
template-driven and the reactive form controls. It's in the `src/app/shared` folder template-driven and the reactive form controls. It's in the `src/app/shared` folder
and declared in the `SharedModule`. and declared in the `SharedModule`.
@ -432,10 +432,10 @@ a#custom-validation
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
and returns a validator function. and returns a validator function.
In this sample, the forbidden name is "bob"; In this sample, the forbidden name is "bob";
the validator rejects any hero name containing "bob". the validator rejects any hero name containing "bob".
Elsewhere it could reject "alice" or any name that the configuring regular expression matches. Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
The `forbiddenNameValidator` factory returns the configured validator function. The `forbiddenNameValidator` factory returns the configured validator function.
That function takes an Angular control object and returns _either_ That function takes an Angular control object and returns _either_
null if the control value is valid _or_ a validation error object. null if the control value is valid _or_ a validation error object.
@ -445,7 +445,7 @@ a#custom-validation
a#custom-validation-directive a#custom-validation-directive
:marked :marked
### Custom validation directive ### Custom validation directive
In the reactive forms component, the `'name'` control's validator function list In the reactive forms component, the `'name'` control's validator function list
has a `forbiddenNameValidator` at the bottom. has a `forbiddenNameValidator` at the bottom.
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','name-validators', 'reactive/hero-form-reactive.component.ts (name validators)')(format='.') +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','name-validators', 'reactive/hero-form-reactive.component.ts (name validators)')(format='.')
:marked :marked
@ -454,7 +454,7 @@ a#custom-validation-directive
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.html','name-input', 'template/hero-form-template2.component.html (name input)')(format='.') +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.html','name-input', 'template/hero-form-template2.component.html (name input)')(format='.')
:marked :marked
The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`. The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`.
Angular `forms` recognizes the directive's role in the validation process because the directive registers itself Angular `forms` recognizes the directive's role in the validation process because the directive registers itself
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives. with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives.
+makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','directive-providers', 'shared/forbidden-name.directive.ts (providers)')(format='.') +makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','directive-providers', 'shared/forbidden-name.directive.ts (providers)')(format='.')
@ -465,22 +465,22 @@ a#custom-validation-directive
:marked :marked
.l-sub-section .l-sub-section
:marked :marked
If you are familiar with Angular validations, you may have noticed If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting` that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of rather than `useClass`. The registered validator must be _this instance_ of
the `ForbiddenValidatorDirective`&mdash;the instance in the form with the `ForbiddenValidatorDirective`&mdash;the instance in the form with
its `forbiddenName` property bound to “bob". If you were to replace its `forbiddenName` property bound to “bob". If you were to replace
`useExisting` with `useClass`, then youd be registering a new class instance, one that `useExisting` with `useClass`, then youd be registering a new class instance, one that
doesnt have a `forbiddenName`. doesnt have a `forbiddenName`.
To see this in action, run the example and then type “bob” in the name of Hero Form 2. To see this in action, run the example and then type “bob” in the name of Hero Form 2.
Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again. Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
This time, when you type “bob”, there's no "bob" error message. This time, when you type “bob”, there's no "bob" error message.
:marked :marked
.l-sub-section .l-sub-section
:marked :marked
For more information on attaching behavior to elements, For more information on attaching behavior to elements,
see [Attribute Directives](../guide/attribute-directives.html). see [Attribute Directives](../guide/attribute-directives.html).
.l-main-section .l-main-section
@ -497,11 +497,11 @@ a#testing
They do not require the `Angular TestBed` or asynchronous testing practices. They do not require the `Angular TestBed` or asynchronous testing practices.
That's not possible with _template-driven_ forms. That's not possible with _template-driven_ forms.
The template-driven approach relies on Angular to produce the control model and The template-driven approach relies on Angular to produce the control model and
to derive validation rules from the HTML validation attributes. to derive validation rules from the HTML validation attributes.
You must use the `Angular TestBed` to create component test instances, You must use the `Angular TestBed` to create component test instances,
write asynchronous tests, and interact with the DOM. write asynchronous tests, and interact with the DOM.
While not difficult, this takes more time, work and While not difficult, this takes more time, work and
skill&mdash;factors that tend to diminish test code skill&mdash;factors that tend to diminish test code
coverage and quality. coverage and quality.

View File

@ -14,6 +14,7 @@ block includes
:marked :marked
Declarations Declarations
* [What classes should I add to _declarations_?](#q-what-to-declare) * [What classes should I add to _declarations_?](#q-what-to-declare)
* [What is a _declarable_?](#q-declarable) * [What is a _declarable_?](#q-declarable)
* [What classes should I _not_ add to _declarations_?](#q-what-not-to-declare) * [What classes should I _not_ add to _declarations_?](#q-what-not-to-declare)
@ -21,17 +22,20 @@ block includes
* [What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?](#q-why-cant-bind-to) * [What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?](#q-why-cant-bind-to)
Imports Imports
* [What should I import?](#q-what-to-import) * [What should I import?](#q-what-to-import)
* [Should I import _BrowserModule_ or _CommonModule_?](#q-browser-vs-common-module) * [Should I import _BrowserModule_ or _CommonModule_?](#q-browser-vs-common-module)
* [What if I import the same module twice?](#q-reimport) * [What if I import the same module twice?](#q-reimport)
Exports Exports
* [What should I export?](#q-what-to-export) * [What should I export?](#q-what-to-export)
* [What should I *not* export?](#q-what-not-to-export) * [What should I *not* export?](#q-what-not-to-export)
* [Can I re-export imported classes and modules?](#q-re-export) * [Can I re-export imported classes and modules?](#q-re-export)
* [What is the _forRoot_ method?](#q-for-root) * [What is the _forRoot_ method?](#q-for-root)
Service Providers Service Providers
* [Why is a service provided in a feature module visible everywhere?](#q-module-provider-visibility) * [Why is a service provided in a feature module visible everywhere?](#q-module-provider-visibility)
* [Why is a service provided in a _lazy-loaded_ module visible only to that module?](#q-lazy-loaded-module-provider-visibility) * [Why is a service provided in a _lazy-loaded_ module visible only to that module?](#q-lazy-loaded-module-provider-visibility)
* [What if two modules provide the same service?](#q-module-provider-duplicates) * [What if two modules provide the same service?](#q-module-provider-duplicates)
@ -43,12 +47,14 @@ block includes
* [How can I tell if a module or service was previously loaded?](#q-is-it-loaded) * [How can I tell if a module or service was previously loaded?](#q-is-it-loaded)
Entry Components Entry Components
* [What is an _entry component_?](#q-entry-component-defined) * [What is an _entry component_?](#q-entry-component-defined)
* [What is the difference between a _bootstrap_ component and an _entry component_?](#q-bootstrap_vs_entry_component) * [What is the difference between a _bootstrap_ component and an _entry component_?](#q-bootstrap_vs_entry_component)
* [When do I add components to _entryComponents_?](#q-when-entry-components) * [When do I add components to _entryComponents_?](#q-when-entry-components)
* [Why does Angular need _entryComponents_?](#q-why-entry-components) * [Why does Angular need _entryComponents_?](#q-why-entry-components)
General General
* [What kinds of modules should I have and how should I use them?](#q-module-recommendations) * [What kinds of modules should I have and how should I use them?](#q-module-recommendations)
* [What's the difference between Angular and JavaScript Modules?](#q-ng-vs-js-modules) * [What's the difference between Angular and JavaScript Modules?](#q-ng-vs-js-modules)
* [How does Angular find components, directives, and pipes in a template?](#q-template-reference) * [How does Angular find components, directives, and pipes in a template?](#q-template-reference)

View File

@ -5,8 +5,8 @@ include ../../../../_includes/_util-fns
in JavaScript. Translating from one language to the other is mostly a in JavaScript. Translating from one language to the other is mostly a
matter of changing the way you organize your code and access Angular APIs. matter of changing the way you organize your code and access Angular APIs.
_TypeScript_ is a popular language option for Angular development. _TypeScript_ is a popular language option for Angular development.
Most code examples on the Internet as well as on this site are written in _TypeScript_. Most code examples on the Internet as well as on this site are written in _TypeScript_.
This cookbook contains recipes for translating _TypeScript_ This cookbook contains recipes for translating _TypeScript_
code examples to _ES6_ and to _ES5_ so that JavaScript developers code examples to _ES6_ and to _ES5_ so that JavaScript developers
can read and write Angular apps in their preferred dialect. can read and write Angular apps in their preferred dialect.
@ -15,16 +15,16 @@ a#toc
:marked :marked
## Table of contents ## Table of contents
[_TypeScript_ to _ES6_ to _ES5_](#from-ts)<br> * [_TypeScript_ to _ES6_ to _ES5_](#from-ts)<br>
[Modularity: imports and exports](#modularity)<br> * [Modularity: imports and exports](#modularity)<br>
[Classes and Class Metadata](#class-metadata)<br> * [Classes and Class Metadata](#class-metadata)<br>
[_ES5_ DSL](#dsl)<br> * [_ES5_ DSL](#dsl)<br>
[Interfaces](#interfaces)<br> * [Interfaces](#interfaces)<br>
[Input and Output Metadata](#io-decorators)<br> * [Input and Output Metadata](#io-decorators)<br>
[Dependency Injection](#dependency-injection)<br> * [Dependency Injection](#dependency-injection)<br>
[Host Binding](#host-binding)<br> * [Host Binding](#host-binding)<br>
[View and Child Decorators](#view-child-decorators)<br> * [View and Child Decorators](#view-child-decorators)<br>
[AOT compilation in _TypeScript_ Only](#aot)<br> * [AOT compilation in _TypeScript_ Only](#aot)<br>
**Run and compare the live <live-example name="cb-ts-to-js">_TypeScript_</live-example> and <live-example name="cb-ts-to-js" lang="js">JavaScript</live-example> **Run and compare the live <live-example name="cb-ts-to-js">_TypeScript_</live-example> and <live-example name="cb-ts-to-js" lang="js">JavaScript</live-example>
code shown in this cookbook.** code shown in this cookbook.**
@ -33,8 +33,8 @@ a#from-ts
.l-main-section .l-main-section
:marked :marked
## _TypeScript_ to _ES6_ to _ES5_ ## _TypeScript_ to _ES6_ to _ES5_
_TypeScript_ _TypeScript_
<a href="https://www.typescriptlang.org" target="_blank" title='"TypeScript is a typed, superset of JavaScript"'>is a typed superset of _ES6 JavaScript_</a>. <a href="https://www.typescriptlang.org" target="_blank" title='"TypeScript is a typed, superset of JavaScript"'>is a typed superset of _ES6 JavaScript_</a>.
_ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers. _ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers.
The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features. The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features.
@ -44,29 +44,29 @@ a#from-ts
* _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_") * _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_")
* _ES6-without-decorators_ to _ES5_ * _ES6-without-decorators_ to _ES5_
When translating from _TypeScript_ to _ES6-with-decorators_, remove When translating from _TypeScript_ to _ES6-with-decorators_, remove
[class property access modifiers](http://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers) [class property access modifiers](http://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers)
such as `public` and `private`. such as `public` and `private`.
Remove most of the Remove most of the
[type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html), [type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html),
such as `:string` and `:boolean` such as `:string` and `:boolean`
but **keep the constructor parameter types which are used for dependency injection**. but **keep the constructor parameter types which are used for dependency injection**.
From _ES6-with-decorators_ to _plain ES6_, remove all
From _ES6-with-decorators_ to _plain ES6_, remove all
[decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
and the remaining types. and the remaining types.
You must declare properties in the class constructor (`this.title = '...'`) rather than in the body of the class. You must declare properties in the class constructor (`this.title = '...'`) rather than in the body of the class.
Finally, from _plain ES6_ to _ES5_, the main missing features are `import` Finally, from _plain ES6_ to _ES5_, the main missing features are `import`
statements and `class` declarations. statements and `class` declarations.
For _plain ES6_ transpilation you can _start_ with a setup similar to the For _plain ES6_ transpilation you can _start_ with a setup similar to the
[_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly. [_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly.
Transpile with [Babel](https://babeljs.io/) using the `es2015` preset. Transpile with [Babel](https://babeljs.io/) using the `es2015` preset.
To use decorators and annotations with Babel, install the To use decorators and annotations with Babel, install the
[`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well. [`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well.
a#modularity a#modularity
.l-main-section .l-main-section
@ -78,7 +78,7 @@ a#modularity
In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements. In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements.
In _ES5_, you access the Angular entities of the [the Angular packages](../glossary.html#scoped-package) In _ES5_, you access the Angular entities of the [the Angular packages](../glossary.html#scoped-package)
through the global `ng` object. through the global `ng` object.
Anything you can import from `@angular` is a nested member of this `ng` object: Anything you can import from `@angular` is a nested member of this `ng` object:
+makeTabs(` +makeTabs(`
@ -100,23 +100,23 @@ a#modularity
Each file in a _TypeScript_ or _ES6_ Angular application constitutes an _ES6_ module. Each file in a _TypeScript_ or _ES6_ Angular application constitutes an _ES6_ module.
When you want to make something available to other modules, you `export` it. When you want to make something available to other modules, you `export` it.
_ES5_ lacks native support for modules. _ES5_ lacks native support for modules.
In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`. In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`.
.alert.is-important .alert.is-important
:marked :marked
The order of `<script>` tags is often significant. The order of `<script>` tags is often significant.
You must load a file that defines a public JavaScript entity before a file that references that entity. You must load a file that defines a public JavaScript entity before a file that references that entity.
:marked :marked
The best practice in _ES5_ is to create a form of modularity that avoids polluting the global scope. The best practice in _ES5_ is to create a form of modularity that avoids polluting the global scope.
Add one application namespace object such as `app` to the global `document`. Add one application namespace object such as `app` to the global `document`.
Then each code file "exports" public entities by attaching them to that namespace object, e.g., `app.HeroComponent`. Then each code file "exports" public entities by attaching them to that namespace object, e.g., `app.HeroComponent`.
You could factor a large application into several sub-namespaces You could factor a large application into several sub-namespaces
which leads to "exports" along the lines of `app.heroQueries.HeroComponent`. which leads to "exports" along the lines of `app.heroQueries.HeroComponent`.
Every _ES5_ file should wrap code in an Every _ES5_ file should wrap code in an
[Immediately Invoked Function Expression (IIFE)](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression) [Immediately Invoked Function Expression (IIFE)](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)
to limit unintentional leaking of private symbols into the global scope. to limit unintentional leaking of private symbols into the global scope.
Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants. Here is a `HeroComponent` as it might be defined and "exported" in each of the four language variants.
+makeTabs(` +makeTabs(`
@ -170,21 +170,21 @@ a#class-metadata
Most Angular _TypeScript_ and _ES6_ code is written as classes. Most Angular _TypeScript_ and _ES6_ code is written as classes.
Properties and method parameters of _TypeScript_ classes may be marked with the access modifiers Properties and method parameters of _TypeScript_ classes may be marked with the access modifiers
`private`, `internal`, and `public`. `private`, `internal`, and `public`.
Remove these modifiers when translating to JavaScript. Remove these modifiers when translating to JavaScript.
Most type declarations (e.g, `:string` and `:boolean`) should be removed when translating to JavaScript. Most type declarations (e.g, `:string` and `:boolean`) should be removed when translating to JavaScript.
When translating to _ES6-with-decorators_, ***do not remove types from constructor parameters!*** When translating to _ES6-with-decorators_, ***do not remove types from constructor parameters!***
Look for types in _TypeScript_ property declarations. Look for types in _TypeScript_ property declarations.
In general it is better to initialize such properties with default values because In general it is better to initialize such properties with default values because
many browser JavaScript engines can generate more performant code. many browser JavaScript engines can generate more performant code.
When _TypeScript_ code follows this same advice, it can infer the property types When _TypeScript_ code follows this same advice, it can infer the property types
and there is nothing to remove during translation. and there is nothing to remove during translation.
In _ES6-without-decorators_, properties of classes must be assigned inside the constructor. In _ES6-without-decorators_, properties of classes must be assigned inside the constructor.
_ES5_ JavaScript has no classes. _ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype. Use the constructor function pattern instead, adding methods to the prototype.
+makeTabs(` +makeTabs(`
@ -203,15 +203,15 @@ a#class-metadata
:marked :marked
### Metadata ### Metadata
When writing in _TypeScript_ or _ES6-with-decorators_, When writing in _TypeScript_ or _ES6-with-decorators_,
provide configuration and metadata by adorning a class with one or more *decorators*. provide configuration and metadata by adorning a class with one or more *decorators*.
For example, you supply metadata to a component class by preceding its definition with a For example, you supply metadata to a component class by preceding its definition with a
[`@Component`](../api/core/index/Component-decorator.html) decorator function whose [`@Component`](../api/core/index/Component-decorator.html) decorator function whose
argument is an object literal with metadata properties. argument is an object literal with metadata properties.
In _plain ES6_, you provide metadata by attaching an `annotations` array to the _class_. In _plain ES6_, you provide metadata by attaching an `annotations` array to the _class_.
Each item in the array is a new instance of a metadata decorator created with a similar metadata object literal. Each item in the array is a new instance of a metadata decorator created with a similar metadata object literal.
In _ES5_, you also provide an `annotations` array but you attach it to the _constructor function_ rather than to a class. In _ES5_, you also provide an `annotations` array but you attach it to the _constructor function_ rather than to a class.
See these variations side-by-side: See these variations side-by-side:
@ -256,8 +256,8 @@ a#dsl
:marked :marked
## _ES5_ DSL ## _ES5_ DSL
This _ES5_ pattern of creating a constructor and annotating it with metadata is so common that Angular This _ES5_ pattern of creating a constructor and annotating it with metadata is so common that Angular
provides a convenience API to make it a little more compact and locates the metadata above the constructor, provides a convenience API to make it a little more compact and locates the metadata above the constructor,
as you would if you wrote in _TypeScript_ or _ES6-with-decorators_. as you would if you wrote in _TypeScript_ or _ES6-with-decorators_.
This _API_ (_Application Programming Interface_) is commonly known as the _ES5 DSL_ (_Domain Specific Language_). This _API_ (_Application Programming Interface_) is commonly known as the _ES5 DSL_ (_Domain Specific Language_).
@ -283,8 +283,8 @@ a#dsl
header Name the constructor header Name the constructor
:marked :marked
A **named** constructor displays clearly in the console log A **named** constructor displays clearly in the console log
if the component throws a runtime error. if the component throws a runtime error.
An **unnamed** constructor displays as an anonymous function (e.g., `class0`) An **unnamed** constructor displays as an anonymous function (e.g., `class0`)
which is impossible to find in the source code. which is impossible to find in the source code.
:marked :marked
@ -306,8 +306,8 @@ a#dsl
:marked :marked
### DSL for other classes ### DSL for other classes
There are similar DSLs for other decorated classes. There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`: You can define a directive with `ng.core.Directive`:
code-example. code-example.
app.MyDirective = ng.core.Directive({ app.MyDirective = ng.core.Directive({
@ -317,7 +317,7 @@ code-example.
}); });
:marked :marked
and a pipe with `ng.core.Pipe`: and a pipe with `ng.core.Pipe`:
code-example. code-example.
app.MyPipe = ng.core.Pipe({ app.MyPipe = ng.core.Pipe({
name: 'myPipe' name: 'myPipe'
}).Class({ }).Class({
@ -330,7 +330,7 @@ a#interfaces
## Interfaces ## Interfaces
A _TypeScript_ interface helps ensure that a class implements the interface's members correctly. A _TypeScript_ interface helps ensure that a class implements the interface's members correctly.
We strongly recommend Angular interfaces where appropriate. We strongly recommend Angular interfaces where appropriate.
For example, the component class that implements the `ngOnInit` lifecycle hook method For example, the component class that implements the `ngOnInit` lifecycle hook method
should implement the `OnInit` interface. should implement the `OnInit` interface.
@ -351,7 +351,7 @@ a#interfaces
ES6 JavaScript, ES6 JavaScript,
ES5 JavaScript, ES5 JavaScript,
ES5 JavaScript with DSL ES5 JavaScript with DSL
`) `)
a#io-decorators a#io-decorators
.l-main-section .l-main-section
@ -361,15 +361,15 @@ a#io-decorators
### Input and Output Decorators ### Input and Output Decorators
In _TypeScript_ and _ES6-with-decorators_, you often add metadata to class _properties_ with _property decorators_. In _TypeScript_ and _ES6-with-decorators_, you often add metadata to class _properties_ with _property decorators_.
For example, you apply [`@Input` and `@Output` property decorators](../guide/template-syntax.html#inputs-outputs) For example, you apply [`@Input` and `@Output` property decorators](../guide/template-syntax.html#inputs-outputs)
to public class properties that will be the target of data binding expressions in parent components. to public class properties that will be the target of data binding expressions in parent components.
There is no equivalent of a property decorator in _ES5_ or _plain ES6_. There is no equivalent of a property decorator in _ES5_ or _plain ES6_.
Fortunately, every property decorator has an equivalent representation in a class decorator metadata property. Fortunately, every property decorator has an equivalent representation in a class decorator metadata property.
A _TypeScript_ `@Input` property decorator can be represented by an item in the `Component` metadata's `inputs` array. A _TypeScript_ `@Input` property decorator can be represented by an item in the `Component` metadata's `inputs` array.
You already know how to add `Component` or `Directive` class metadata in _any_ JavaScript dialect so You already know how to add `Component` or `Directive` class metadata in _any_ JavaScript dialect so
there's nothing fundamentally new about adding another property. there's nothing fundamentally new about adding another property.
But note that what would have been _separate_ `@Input` and `@Output` property decorators for each class property are But note that what would have been _separate_ `@Input` and `@Output` property decorators for each class property are
combined in the metadata `inputs` and `outputs` _arrays_. combined in the metadata `inputs` and `outputs` _arrays_.
@ -388,21 +388,21 @@ a#io-decorators
ES5 JavaScript with DSL ES5 JavaScript with DSL
`) `)
:marked :marked
In the previous example, one of the public-facing binding names (`cancelMsg`) In the previous example, one of the public-facing binding names (`cancelMsg`)
differs from the corresponding class property name (`notOkMsg`). differs from the corresponding class property name (`notOkMsg`).
That's OK but you must tell Angular about it so that it can map an external binding of `cancelMsg` That's OK but you must tell Angular about it so that it can map an external binding of `cancelMsg`
to the component's `notOkMsg` property. to the component's `notOkMsg` property.
In _TypeScript_ and _ES6-with-decorators_, In _TypeScript_ and _ES6-with-decorators_,
you specify the special binding name in the argument to the property decorator. you specify the special binding name in the argument to the property decorator.
In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata. In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata.
.l-main-section .l-main-section
:marked :marked
## Dependency Injection ## Dependency Injection
Angular relies heavily on [Dependency Injection](../guide/dependency-injection.html) to provide services to the objects it creates. Angular relies heavily on [Dependency Injection](../guide/dependency-injection.html) to provide services to the objects it creates.
When Angular creates a new component, directive, pipe or another service, When Angular creates a new component, directive, pipe or another service,
it sets the class constructor parameters to instances of services provided by an _Injector_. it sets the class constructor parameters to instances of services provided by an _Injector_.
The developer must tell Angular what to inject into each parameter. The developer must tell Angular what to inject into each parameter.
@ -410,26 +410,26 @@ a#io-decorators
### Injection by Class Type ### Injection by Class Type
The easiest and most popular technique in _TypeScript_ and _ES6-with-decorators_ is to set the constructor parameter type The easiest and most popular technique in _TypeScript_ and _ES6-with-decorators_ is to set the constructor parameter type
to the class associated with the service to inject. to the class associated with the service to inject.
The _TypeScript_ transpiler writes parameter type information into the generated JavaScript. The _TypeScript_ transpiler writes parameter type information into the generated JavaScript.
Angular reads that information at runtime and locates the corresponding service in the appropriate _Injector_.. Angular reads that information at runtime and locates the corresponding service in the appropriate _Injector_..
The _ES6-with-decorators_ transpiler does essentially the same thing using the same parameter-typing syntax. The _ES6-with-decorators_ transpiler does essentially the same thing using the same parameter-typing syntax.
_ES5_ and _plain ES6_ lack types so you must identify "injectables" by attaching a **`parameters`** array to the constructor function. _ES5_ and _plain ES6_ lack types so you must identify "injectables" by attaching a **`parameters`** array to the constructor function.
Each item in the array specifies the service's injection token. Each item in the array specifies the service's injection token.
As with _TypeScript_ the most popular token is a class, As with _TypeScript_ the most popular token is a class,
or rather a _constructor function_ that represents a class in _ES5_ and _plain ES6_. or rather a _constructor function_ that represents a class in _ES5_ and _plain ES6_.
The format of the `parameters` array varies: The format of the `parameters` array varies:
* _plain ES6_ &mdash; nest each constructor function in a sub-array. * _plain ES6_ &mdash; nest each constructor function in a sub-array.
* _ES5_ &mdash; simply list the constructor functions. * _ES5_ &mdash; simply list the constructor functions.
When writing with _ES5 DSL_, set the `Class.constructor` property to When writing with _ES5 DSL_, set the `Class.constructor` property to
an array whose first parameters are the injectable constructor functions and whose an array whose first parameters are the injectable constructor functions and whose
last parameter is the class constructor itself. last parameter is the class constructor itself.
This format should be familiar to AngularJS developers. This format should be familiar to AngularJS developers.
+makeTabs(` +makeTabs(`
@ -451,20 +451,20 @@ a#io-decorators
### Injection with the @Inject decorator ### Injection with the @Inject decorator
Sometimes the dependency injection token isn't a class or constructor function. Sometimes the dependency injection token isn't a class or constructor function.
In _TypeScript_ and _ES6-with-decorators_, you precede the class constructor parameter In _TypeScript_ and _ES6-with-decorators_, you precede the class constructor parameter
by calling the `@Inject()` decorator with the injection token. by calling the `@Inject()` decorator with the injection token.
In the following example, the token is the string `'heroName'`. In the following example, the token is the string `'heroName'`.
The other JavaScript dialects add a `parameters` array to the class contructor function. The other JavaScript dialects add a `parameters` array to the class contructor function.
Each item constains a new instance of `Inject`: Each item constains a new instance of `Inject`:
* _plain ES6_ &mdash; each item is a new instance of `Inject(token)` in a sub-array. * _plain ES6_ &mdash; each item is a new instance of `Inject(token)` in a sub-array.
* _ES5_ &mdash; simply list the string tokens. * _ES5_ &mdash; simply list the string tokens.
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter. array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
+makeTabs(` +makeTabs(`
cb-ts-to-js/ts/src/app/hero-di-inject.component.ts, cb-ts-to-js/ts/src/app/hero-di-inject.component.ts,
@ -486,7 +486,7 @@ a#io-decorators
You can qualify injection behavior with injection decorators from `@angular/core`. You can qualify injection behavior with injection decorators from `@angular/core`.
In _TypeScript_ and _ES6-with-decorators_, In _TypeScript_ and _ES6-with-decorators_,
you precede the constructor parameters with injection qualifiers such as: you precede the constructor parameters with injection qualifiers such as:
* [`@Optional`](../api/core/index/Optional-decorator.html) sets the parameter to `null` if the service is missing * [`@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 * [`@Attribute`](../api/core/index/Attribute-interface.html) to inject a host element attribute value
@ -497,7 +497,7 @@ a#io-decorators
In _plain ES6_ and _ES5_, create an instance of the equivalent injection qualifier in a nested array within the `parameters` array. In _plain ES6_ and _ES5_, create an instance of the equivalent injection qualifier in a nested array within the `parameters` array.
For example, you'd write `new Optional()` in _plain ES6_ and `new ng.core.Optional()` in _ES5_. For example, you'd write `new Optional()` in _plain ES6_ and `new ng.core.Optional()` in _ES5_.
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
array as before. Use a nested array to define a parameter's complete injection specification. array as before. Use a nested array to define a parameter's complete injection specification.
@ -520,7 +520,7 @@ a#io-decorators
:marked :marked
In the example above, there is no provider for the `'titlePrefix'` token. In the example above, there is no provider for the `'titlePrefix'` token.
Without `Optional`, Angular would raise an error. Without `Optional`, Angular would raise an error.
With `Optional`, Angular sets the constructor parameter to `null` With `Optional`, Angular sets the constructor parameter to `null`
and the component displays the title without a prefix. and the component displays the title without a prefix.
a#host-binding a#host-binding
@ -535,15 +535,15 @@ a#host-binding
In _TypeScript_ and _ES6-with-decorators_, you can use host property decorators to bind a host In _TypeScript_ and _ES6-with-decorators_, you can use host property decorators to bind a host
element to a component or directive. element to a component or directive.
The [`@HostBinding`](../api/core/index/HostBinding-interface.html) decorator The [`@HostBinding`](../api/core/index/HostBinding-interface.html) decorator
binds host element properties to component data properties. binds host element properties to component data properties.
The [`@HostListener`](../api/core/index/HostListener-interface.html) decorator binds The [`@HostListener`](../api/core/index/HostListener-interface.html) decorator binds
host element events to component event handlers. host element events to component event handlers.
In _plain ES6_ or _ES5_, add a `host` attribute to the component metadata to achieve the In _plain ES6_ or _ES5_, add a `host` attribute to the component metadata to achieve the
same effect as `@HostBinding` and `@HostListener`. same effect as `@HostBinding` and `@HostListener`.
The `host` value is an object whose properties are host property and listener bindings: The `host` value is an object whose properties are host property and listener bindings:
* Each key follows regular Angular binding syntax: `[property]` for host bindings * Each key follows regular Angular binding syntax: `[property]` for host bindings
or `(event)` for host listeners. or `(event)` for host listeners.
* Each value identifies the corresponding component property or method. * Each value identifies the corresponding component property or method.
@ -566,7 +566,7 @@ a#host-binding
:marked :marked
### Host Metadata ### Host Metadata
Some developers prefer to specify host properties and listeners Some developers prefer to specify host properties and listeners
in the component metadata. in the component metadata.
They'd _rather_ do it the way you _must_ do it _ES5_ and _plain ES6_. They'd _rather_ do it the way you _must_ do it _ES5_ and _plain ES6_.
The following re-implementation of the `HeroComponent` reminds us that _any property metadata decorator_ The following re-implementation of the `HeroComponent` reminds us that _any property metadata decorator_
@ -585,22 +585,22 @@ a#view-child-decorators
.l-main-section .l-main-section
:marked :marked
### View and Child Decorators ### View and Child Decorators
Several _property_ decorators query a component's nested view and content components. Several _property_ decorators query a component's nested view and content components.
.l-sub-section .l-sub-section
:marked :marked
_View_ children are associated with element tags that appear _within_ the component's template. _View_ children are associated with element tags that appear _within_ the component's template.
_Content_ children are associated with elements that appear _between_ the component's element tags; _Content_ children are associated with elements that appear _between_ the component's element tags;
they are projected into an `<ng-content>` slot in the component's template. they are projected into an `<ng-content>` slot in the component's template.
:marked :marked
The [`@ViewChild`](../api/core/index/ViewChild-decorator.html) and The [`@ViewChild`](../api/core/index/ViewChild-decorator.html) and
[`@ViewChildren`](../api/core/index/ViewChildren-decorator.html) property decorators [`@ViewChildren`](../api/core/index/ViewChildren-decorator.html) property decorators
allow a component to query instances of other components that are used in allow a component to query instances of other components that are used in
its view. its view.
In _ES5_ and _ES6_, you access a component's view children by adding a `queries` property to the component metadata. In _ES5_ and _ES6_, you access a component's view children by adding a `queries` property to the component metadata.
The `queries` property value is a hash map. The `queries` property value is a hash map.
* each _key_ is the name of a component property that will hold the view child or children. * each _key_ is the name of a component property that will hold the view child or children.
@ -643,15 +643,15 @@ a#view-child-decorators
.alert.is-helpful .alert.is-helpful
:marked :marked
In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata
instead of the `@ViewChild` and `@ContentChild` property decorators. instead of the `@ViewChild` and `@ContentChild` property decorators.
a#aot a#aot
.l-main-section .l-main-section
:marked :marked
## AOT Compilation in _TypeScript_ only ## AOT Compilation in _TypeScript_ only
Angular offers two modes of template compilation, JIT (_Just-in-Time_) and Angular offers two modes of template compilation, JIT (_Just-in-Time_) and
[AOT (_Ahead-of-Time_)](aot-compiler.html). [AOT (_Ahead-of-Time_)](aot-compiler.html).
Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates
_TypeScript_ files as an intermediate result. _TypeScript_ files as an intermediate result.

View File

@ -12,6 +12,7 @@ block includes
and [how to use it](#angular-di) in an Angular app. and [how to use it](#angular-di) in an Angular app.
:marked :marked
# Contents # Contents
- [Why dependency injection?](#why-di) - [Why dependency injection?](#why-di)
- [Angular dependency injection](#angular-dependency-injection) - [Angular dependency injection](#angular-dependency-injection)
- [Configuring the injector](#injector-config) - [Configuring the injector](#injector-config)

View File

@ -1,11 +1,12 @@
include ../_util-fns include ../_util-fns
:marked :marked
This page describes tools and techniques for deploy and optimize your Angular application. This page describes tools and techniques for deploy and optimize your Angular application.
a#toc a#toc
:marked :marked
## Table of contents ## Table of contents
* [Overview](#overview) * [Overview](#overview)
* [Simplest deployment possible](#dev-deploy) * [Simplest deployment possible](#dev-deploy)
* [Optimize for production](#optimize) * [Optimize for production](#optimize)
@ -19,7 +20,7 @@ a#toc
* [Enable production mode](#enable-prod-mode) * [Enable production mode](#enable-prod-mode)
* [Lazy loading](#lazy-loading) * [Lazy loading](#lazy-loading)
* [Server configuration](#server-configuration) * [Server configuration](#server-configuration)
* [Routed apps must fallback to `index.html`](#fallback) * [Routed apps must fallback to `index.html`](#fallback)
* [CORS: requesting services from a different server](#cors) * [CORS: requesting services from a different server](#cors)
a#overview a#overview
@ -32,10 +33,10 @@ a#overview
* The [simple way](#dev-deploy "Simplest deployment possible") is to copy the development environment to the server. * The [simple way](#dev-deploy "Simplest deployment possible") is to copy the development environment to the server.
* [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of * [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of
[several optimization strategies](#optimize). [several optimization strategies](#optimize).
You'll also want to read the [detailed instructions in the AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook"). You'll also want to read the [detailed instructions in the AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook").
* [Webpack](#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT. * [Webpack](#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
The Angular [webpack guide](webpack.html "Webpack: an introduction") can get you started and The Angular [webpack guide](webpack.html "Webpack: an introduction") can get you started and
_this_ page provides additional optimization advice, but you'll probably have to learn more about webpack on your own. _this_ page provides additional optimization advice, but you'll probably have to learn more about webpack on your own.
@ -49,20 +50,20 @@ a#overview
.l-main-section .l-main-section
a#dev-deploy a#dev-deploy
:marked :marked
## Simplest deployment possible ## Simplest deployment possible
The simplest way to deploy the app is to publish it to a web server The simplest way to deploy the app is to publish it to a web server
directly out of the development environment. directly out of the development environment.
It's already running locally. You'll just copy it, almost _as is_, It's already running locally. You'll just copy it, almost _as is_,
to a non-local server that others can reach. to a non-local server that others can reach.
1. Copy _everything_ (or [_almost_ everything](#node-modules "Loading npm packages from the web")) 1. Copy _everything_ (or [_almost_ everything](#node-modules "Loading npm packages from the web"))
from the local project folder to a folder on the server. from the local project folder to a folder on the server.
1. If you're serving the app out of a subfolder, 1. If you're serving the app out of a subfolder,
edit a version of `index.html` to set the `<base href>` appropriately. edit a version of `index.html` to set the `<base href>` appropriately.
For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to
`<base href="/my/app/">`. `<base href="/my/app/">`.
Otherwise, leave it alone. Otherwise, leave it alone.
[More on this below](#base-tag). [More on this below](#base-tag).
@ -85,12 +86,12 @@ a#node-modules
:marked :marked
### Load npm package files from the web (SystemJS) ### Load npm package files from the web (SystemJS)
The `node_modules` folder of _npm packages_ contains much more code The `node_modules` folder of _npm packages_ contains much more code
than is needed to actually run your app in the browser. than is needed to actually run your app in the browser.
The `node_modules` for the Quickstart installation is typically 20,500+ files and 180+ MB. The `node_modules` for the Quickstart installation is typically 20,500+ files and 180+ MB.
The application itself requires a tiny fraction of that to run. The application itself requires a tiny fraction of that to run.
It takes a long time to upload all of that useless bulk and It takes a long time to upload all of that useless bulk and
users will wait unnecessarily while library files download piecemeal. users will wait unnecessarily while library files download piecemeal.
Load the few files you need from the web instead. Load the few files you need from the web instead.
@ -100,12 +101,12 @@ a#node-modules
+makeExample('deployment/ts/src/index.html', 'node-module-scripts', '')(format=".") +makeExample('deployment/ts/src/index.html', 'node-module-scripts', '')(format=".")
:marked :marked
(2) Replace the `systemjs.config.js` script with a script that (2) Replace the `systemjs.config.js` script with a script that
loads `systemjs.config.server.js`. loads `systemjs.config.server.js`.
+makeExample('deployment/ts/src/index.html', 'systemjs-config', '')(format=".") +makeExample('deployment/ts/src/index.html', 'systemjs-config', '')(format=".")
:marked :marked
(3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder. (3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder.
This alternative version configures _SystemJS_ to load _UMD_ versions of Angular This alternative version configures _SystemJS_ to load _UMD_ versions of Angular
(and other third-party packages) from the web. (and other third-party packages) from the web.
Modify `systemjs.config.server.js` as necessary to stay in sync with changes Modify `systemjs.config.server.js` as necessary to stay in sync with changes
@ -117,15 +118,15 @@ a#node-modules
:marked :marked
In the standard SystemJS config, the `npm` path points to the `node_modules/`. In the standard SystemJS config, the `npm` path points to the `node_modules/`.
In this server config, it points to In this server config, it points to
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>, <a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
a site that hosts _npm packages_, a site that hosts _npm packages_,
and loads them from the web directly. and loads them from the web directly.
There are other service providers that do the same thing. There are other service providers that do the same thing.
If you are unwilling or unable to load packages from the open web, If you are unwilling or unable to load packages from the open web,
the inventory in `systemjs.config.server.js` identifies the files and folders that the inventory in `systemjs.config.server.js` identifies the files and folders that
you would copy to a library folder on the server. you would copy to a library folder on the server.
Then change the config's `'npm'` path to point to that folder. Then change the config's `'npm'` path to point to that folder.
### Practice with an example ### Practice with an example
@ -140,7 +141,7 @@ a#node-modules
deployment/ts/src/app/app.component.ts, deployment/ts/src/app/app.component.ts,
deployment/ts/src/app/crisis-list.component.ts, deployment/ts/src/app/crisis-list.component.ts,
deployment/ts/src/app/hero-list.component.ts deployment/ts/src/app/hero-list.component.ts
`, `,
null, null,
`index.html, `index.html,
systemjs.config.server.js, systemjs.config.server.js,
@ -161,9 +162,9 @@ a#node-modules
1. Run it with `npm start` as you would any project. 1. Run it with `npm start` as you would any project.
1. Inspect the network traffic in the browser developer tools. 1. Inspect the network traffic in the browser developer tools.
Notice that it loads all packages from the web. Notice that it loads all packages from the web.
You could delete the `node_modules` folder and the app would still run You could delete the `node_modules` folder and the app would still run
(although you wouldn't be able to recompile or launch `lite-server` (although you wouldn't be able to recompile or launch `lite-server`
until you restored it). until you restored it).
@ -177,26 +178,26 @@ a#optimize
## Optimize for production ## Optimize for production
Although deploying directly from the development environment works, it's far from optimal. Although deploying directly from the development environment works, it's far from optimal.
The client makes many small requests for individual application code and template files, The client makes many small requests for individual application code and template files,
a fact you can quickly confirm by looking at the network tab in a browser's developer tools. a fact you can quickly confirm by looking at the network tab in a browser's developer tools.
Each small file download can spend more time communicating with the server than tranfering data. Each small file download can spend more time communicating with the server than tranfering data.
Development files are full of comments and whitespace for easy reading and debugging. Development files are full of comments and whitespace for easy reading and debugging.
The browser downloads entire libraries, instead of just the parts the app needs. The browser downloads entire libraries, instead of just the parts the app needs.
The volume of code passed from server to client (the "payload") The volume of code passed from server to client (the "payload")
can be significantly larger than is strictly necessary to execute the application. can be significantly larger than is strictly necessary to execute the application.
The many requests and large payloads mean The many requests and large payloads mean
the app takes longer to launch than it would if you optimized it. the app takes longer to launch than it would if you optimized it.
Several seconds may pass (or worse) before the user can see or do anything userful. Several seconds may pass (or worse) before the user can see or do anything userful.
Does it matter? That depends upon business and technical factors you must evaluate for yourself. Does it matter? That depends upon business and technical factors you must evaluate for yourself.
If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses. If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses.
- Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates. - Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
- Bundling: concatenates modules into a single file (bundle). - Bundling: concatenates modules into a single file (bundle).
- Inlining: pulls template html and css into the components. - Inlining: pulls template html and css into the components.
- Minification: removes excess whitespace, comments, and optional tokens. - Minification: removes excess whitespace, comments, and optional tokens.
- Uglification: rewrites code to use short, cryptic variable and function names. - Uglification: rewrites code to use short, cryptic variable and function names.
@ -204,11 +205,11 @@ a#optimize
- Pruned libraries: drop unused libraries and pare others down to the features you need. - Pruned libraries: drop unused libraries and pare others down to the features you need.
- Performance measurement: focus on optimizations that make a measurable difference. - Performance measurement: focus on optimizations that make a measurable difference.
Each tool does something different. Each tool does something different.
They work best in combination and are mutually reinforcing. They work best in combination and are mutually reinforcing.
You can use any build system you like. You can use any build system you like.
Whatever system you choose, be sure to automate it so that Whatever system you choose, be sure to automate it so that
building for production is a single step. building for production is a single step.
a#aot a#aot
@ -227,20 +228,20 @@ a#aot
Learn more about AOT Compilation in the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook") Learn more about AOT Compilation in the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook")
which describes running the AOT compiler from the command line which describes running the AOT compiler from the command line
and using [_rollup_](#rollup) for bundling, minification, uglification and tree shaking. and using [_rollup_](#rollup) for bundling, minification, uglification and tree shaking.
a#webpack a#webpack
:marked :marked
### Webpack (and AOT) ### Webpack (and AOT)
<a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another <a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another
great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application. great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application.
The "[Webpack: an introduction](webpack.html "Webpack: an introduction")" guide will get you started The "[Webpack: an introduction](webpack.html "Webpack: an introduction")" guide will get you started
using webpack with Angular. using webpack with Angular.
Consider configuring _Webpack_ with the official Consider configuring _Webpack_ with the official
<a href="https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack" target="_blank" title="Ahead-of-Time Webpack Plugin"> <a href="https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack" target="_blank" title="Ahead-of-Time Webpack Plugin">
Angular Ahead-of-Time Webpack Plugin</a>. Angular Ahead-of-Time Webpack Plugin</a>.
This plugin transpiles the TypeScript application code, This plugin transpiles the TypeScript application code,
bundles lazy loaded `NgModules` separately, bundles lazy loaded `NgModules` separately,
and performs AOT compilation &mdash; without any changes to the source code. and performs AOT compilation &mdash; without any changes to the source code.
@ -250,12 +251,12 @@ a#rollup
Any code that you don't call is _dead code_. Any code that you don't call is _dead code_.
You can reduce the total size of the application substantially by removing dead code from the application and from third-party libraries. You can reduce the total size of the application substantially by removing dead code from the application and from third-party libraries.
_Tree shaking_ is a _dead code elimination_ technique that removes entire exports from JavaScript modules. _Tree shaking_ is a _dead code elimination_ technique that removes entire exports from JavaScript modules.
If a library exports something that the application doesn't import, a tree shaking tool removes it from the code base. If a library exports something that the application doesn't import, a tree shaking tool removes it from the code base.
Tree shaking was popularized by Tree shaking was popularized by
<a href="http://rollupjs.org/" target="_blank" title="Rollup">Rollup</a>, a popular tool with an ecosystem of <a href="http://rollupjs.org/" target="_blank" title="Rollup">Rollup</a>, a popular tool with an ecosystem of
plugins for bundling, minification, and uglification. plugins for bundling, minification, and uglification.
Learn more about tree shaking and dead code elmination in Learn more about tree shaking and dead code elmination in
<a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" target="_blank" title="Tree-shaking and Dead Code Elimination"> <a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" target="_blank" title="Tree-shaking and Dead Code Elimination">
@ -280,15 +281,15 @@ a#measure
You can make better decisions about what to optimize and how when you have a clear and accurate understanding of You can make better decisions about what to optimize and how when you have a clear and accurate understanding of
what's making the application slow. what's making the application slow.
The cause may not be what you think it is. The cause may not be what you think it is.
You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower. You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower.
You should measure the app's actual behavior when running in the environments that are important to you. You should measure the app's actual behavior when running in the environments that are important to you.
The The
<a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" title="Chrome DevTools Network Performance"> <a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" title="Chrome DevTools Network Performance">
Chrome DevTools Network Performance page</a> is a good place to start learning about measuring performance. Chrome DevTools Network Performance page</a> is a good place to start learning about measuring performance.
The [WebPageTest](https://www.webpagetest.org/) tool is another good choice The [WebPageTest](https://www.webpagetest.org/) tool is another good choice
that can also help verify that your deployment was successful. that can also help verify that your deployment was successful.
a#angular-configuration a#angular-configuration
@ -301,10 +302,10 @@ a#angular-configuration
a#base-tag a#base-tag
:marked :marked
### The `base` tag ### The `base` tag
The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href) The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets. specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg` For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
into a server request for `my/app/some/place/foo.jpg`. into a server request for `my/app/some/place/foo.jpg`.
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files. During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
@ -312,11 +313,11 @@ a#base-tag
:marked :marked
See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API: APP_BASE_HREF") alternative. See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API: APP_BASE_HREF") alternative.
:marked :marked
In development, you typically start the server in the folder that holds `index.html`. In development, you typically start the server in the folder that holds `index.html`.
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app. That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
But on the shared or production server, you might serve the app from a subfolder. But on the shared or production server, you might serve the app from a subfolder.
For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`, For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`,
the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`. the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`.
When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors
@ -334,7 +335,7 @@ code-example(format="nocode").
:marked :marked
Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles. Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles.
To enable [production mode](../api/core/index/enableProdMode-function.html) when running remotely, add the following code to the `main.ts`. To enable [production mode](../api/core/index/enableProdMode-function.html) when running remotely, add the following code to the `main.ts`.
+makeExample('src/main.ts', 'enableProdMode','src/main.ts (enableProdMode)')(format=".") +makeExample('src/main.ts', 'enableProdMode','src/main.ts (enableProdMode)')(format=".")
@ -345,7 +346,7 @@ a#lazy-loading
You can dramatically reduce launch time by only loading the application modules that You can dramatically reduce launch time by only loading the application modules that
absolutely must be present when the app starts. absolutely must be present when the app starts.
Configure the Angular Router to defer loading of all other modules (and their associated code), either by Configure the Angular Router to defer loading of all other modules (and their associated code), either by
[waiting until the app has launched](router.html#preloading "Preloading") [waiting until the app has launched](router.html#preloading "Preloading")
or by [_lazy loading_](router.html#asynchronous-routing "Lazy loading") or by [_lazy loading_](router.html#asynchronous-routing "Lazy loading")
them on demand. them on demand.
@ -356,14 +357,14 @@ a#lazy-loading
You've arranged to lazy load a module. You've arranged to lazy load a module.
But you unintentionally import it, with a JavaScript `import` statement, But you unintentionally import it, with a JavaScript `import` statement,
in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`. in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`.
If you do that, the module will be loaded immediately. If you do that, the module will be loaded immediately.
The bundling configuration must take lazy loading into consideration. The bundling configuration must take lazy loading into consideration.
Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default. Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default.
Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules. Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules.
You have to create these bundles manually. You have to create these bundles manually.
The The
[Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack) [Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)
automatically recognizes lazy loaded `NgModules` and creates separate bundles for them. automatically recognizes lazy loaded `NgModules` and creates separate bundles for them.
@ -372,7 +373,7 @@ a#server-configuration
.l-main-section .l-main-section
:marked :marked
## Server configuration ## Server configuration
This section covers changes you may have make to the server or to files deployed to the server. This section covers changes you may have make to the server or to files deployed to the server.
a#fallback a#fallback
@ -383,7 +384,7 @@ a#fallback
You don't need a server-side engine to dynamically compose application pages because You don't need a server-side engine to dynamically compose application pages because
Angular does that on the client-side. Angular does that on the client-side.
If the app uses the Angular router, you must configure the server If the app uses the Angular router, you must configure the server
to return the application's host page (`index.html`) when asked for a file that it does not have. to return the application's host page (`index.html`) when asked for a file that it does not have.
a#deep-link a#deep-link
@ -396,27 +397,27 @@ a#deep-link
There is no issue when the user navigates to that URL from within a running client. There is no issue when the user navigates to that URL from within a running client.
The Angular router interprets the URL and routes to that page and hero. The Angular router interprets the URL and routes to that page and hero.
But clicking a link in an email, entering it in the browser address bar, But clicking a link in an email, entering it in the browser address bar,
or merely refreshing the browser while on the hero detail page &mdash; or merely refreshing the browser while on the hero detail page &mdash;
all of these actions are handled by the browser itself, _outside_ the running application. all of these actions are handled by the browser itself, _outside_ the running application.
The browser makes a direct request to the server for that URL, bypassing the router. The browser makes a direct request to the server for that URL, bypassing the router.
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`. A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
configured to return `index.html` instead. configured to return `index.html` instead.
#### Fallback configuration examples #### Fallback configuration examples
There is no single configuration that works for every server. There is no single configuration that works for every server.
The following sections describe configurations for some of the most popular servers. The following sections describe configurations for some of the most popular servers.
The list is by no means exhaustive, but should provide you with a good starting point. The list is by no means exhaustive, but should provide you with a good starting point.
#### Development servers #### Development servers
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the - [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`. [Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the - [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
`historyApiFallback` entry in the dev server options as follows: `historyApiFallback` entry in the dev server options as follows:
code-example(). code-example().
@ -427,13 +428,13 @@ code-example().
:marked :marked
#### Production servers #### Production servers
- [Apache](https://httpd.apache.org/): add a - [Apache](https://httpd.apache.org/): add a
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) [rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
to the `.htaccess` file as show to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/): [here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
code-example(format="."). code-example(format=".").
RewriteEngine On RewriteEngine On
# If an existing asset or directory is requested go to it as it is # If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
@ -443,7 +444,7 @@ code-example(format=".").
RewriteRule ^ /index.html RewriteRule ^ /index.html
:marked :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), [Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
modified to serve `index.html`: modified to serve `index.html`:
@ -470,16 +471,16 @@ code-example(format="." escape="html").
</system.webServer> </system.webServer>
:marked :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) [directly configure](https://github.com/isaacs/github/issues/408)
the GitHub Pages server, but you can add a 404 page. the GitHub Pages server, but you can add a 404 page.
Copy `index.html` into `404.html`. Copy `index.html` into `404.html`.
It will still be served as the 404 response, but the browser will process that page and load the app properly. It will still be served as the 404 response, but the browser will process that page and load the app properly.
It's also a good idea to It's also a good idea to
[serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch) [serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
and to and to
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm) [create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a - [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites). [rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
@ -493,7 +494,7 @@ a#cors
.l-main-section .l-main-section
:marked :marked
### Requesting services from a different server (CORS) ### Requesting services from a different server (CORS)
Angular developers may encounter a Angular developers may encounter a
<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" target="_blank" title="Cross-origin resource sharing"> <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" target="_blank" title="Cross-origin resource sharing">
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request). <i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request).
@ -501,7 +502,7 @@ a#cors
Browsers forbid such requests unless the server permits them explicitly. Browsers forbid such requests unless the server permits them explicitly.
There isn't anything the client application can do about these errors. There isn't anything the client application can do about these errors.
The server must be configured to accept the application's requests. The server must be configured to accept the application's requests.
Read about how to enable CORS for specific servers at Read about how to enable CORS for specific servers at
<a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>. <a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>.

View File

@ -8,7 +8,7 @@ figure
:marked :marked
A component has a lifecycle managed by Angular. A component has a lifecycle managed by Angular.
Angular creates it, renders it, creates and renders its children, Angular creates it, renders it, creates and renders its children,
checks it when its data-bound properties change, and destroys it before removing it from the DOM. checks it when its data-bound properties change, and destroys it before removing it from the DOM.
@ -20,6 +20,7 @@ figure
+ifDocsFor('ts|js') +ifDocsFor('ts|js')
:marked :marked
## Contents ## Contents
* [Component lifecycle hooks overview](#hooks-overview) * [Component lifecycle hooks overview](#hooks-overview)
* [Lifecycle sequence](#hooks-purpose-timing) * [Lifecycle sequence](#hooks-purpose-timing)
* [Interfaces are optional (technically)](#interface-optional) * [Interfaces are optional (technically)](#interface-optional)
@ -37,7 +38,7 @@ figure
* [Content projection](#content-projection) * [Content projection](#content-projection)
* [AfterContent hooks](#aftercontent-hooks) * [AfterContent hooks](#aftercontent-hooks)
* [No unidirectional flow worries with _AfterContent_](#no-unidirectional-flow-worries) * [No unidirectional flow worries with _AfterContent_](#no-unidirectional-flow-worries)
:marked :marked
Try the <live-example></live-example>. Try the <live-example></live-example>.
@ -51,7 +52,7 @@ a#hooks-overview
one or more of the *lifecycle hook* interfaces in the Angular `core` library. one or more of the *lifecycle hook* interfaces in the Angular `core` library.
Each interface has a single hook method whose name is the interface name prefixed with `ng`. Each interface has a single hook method whose name is the interface name prefixed with `ng`.
For example, the `OnInit` interface has a hook method named `ngOnInit()` For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component: that Angular calls shortly after creating the component:
+makeExample('lifecycle-hooks/ts/src/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.') +makeExample('lifecycle-hooks/ts/src/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.')
:marked :marked
@ -77,7 +78,7 @@ table(width="100%")
:marked :marked
Respond when Angular (re)sets data-bound input properties. Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values. The method receives a `SimpleChanges` object of current and previous property values.
Called before `ngOnInit()` and whenever one or more data-bound input properties change. Called before `ngOnInit()` and whenever one or more data-bound input properties change.
tr(style=top) tr(style=top)
@ -86,15 +87,15 @@ table(width="100%")
:marked :marked
Initialize the directive/component after Angular first displays the data-bound properties Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties. and sets the directive/component's input properties.
Called _once_, after the _first_ `ngOnChanges()`. Called _once_, after the _first_ `ngOnChanges()`.
tr(style=top) tr(style=top)
td <code>ngDoCheck()</code> td <code>ngDoCheck()</code>
td td
:marked :marked
Detect and act upon changes that Angular can't or won't detect on its own. Detect and act upon changes that Angular can't or won't detect on its own.
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`. Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
tr(style=top) tr(style=top)
@ -142,13 +143,13 @@ table(width="100%")
td td
:marked :marked
Cleanup just before Angular destroys the directive/component. Cleanup just before Angular destroys the directive/component.
Unsubscribe Observables and detach event handlers to avoid memory leaks. Unsubscribe Observables and detach event handlers to avoid memory leaks.
Called _just before_ Angular destroys the directive/component. Called _just before_ Angular destroys the directive/component.
+ifDocsFor('ts|js') +ifDocsFor('ts|js')
a#interface-optional a#interface-optional
.l-main-section .l-main-section
:marked :marked
## Interfaces are optional (technically) ## Interfaces are optional (technically)
@ -164,7 +165,7 @@ table(width="100%")
Nonetheless, it's good practice to add interfaces to TypeScript directive classes Nonetheless, it's good practice to add interfaces to TypeScript directive classes
in order to benefit from strong typing and editor tooling. in order to benefit from strong typing and editor tooling.
a#other-lifecycle-hooks a#other-lifecycle-hooks
.l-main-section .l-main-section
:marked :marked
@ -344,7 +345,7 @@ a#oninit
.l-sub-section .l-sub-section
:marked :marked
Misko Hevery, Angular team lead, Misko Hevery, Angular team lead,
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/) [explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
you should avoid complex constructor logic. you should avoid complex constructor logic.
:marked :marked
@ -352,9 +353,9 @@ a#oninit
You shouldn't worry that a new component will try to contact a remote server when You shouldn't worry that a new component will try to contact a remote server when
created under test or before you decide to display it. created under test or before you decide to display it.
Constructors should do no more than set the initial local variables to simple values. Constructors should do no more than set the initial local variables to simple values.
An `ngOnInit()` is a good place for a component to fetch its initial data. The An `ngOnInit()` is a good place for a component to fetch its initial data. The
[Tour of Heroes Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP Client](server-communication.html#oninit) [Tour of Heroes Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP Client](server-communication.html#oninit)
guides show how. guides show how.
@ -409,7 +410,7 @@ figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges") img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
:marked :marked
The log entries appear as the string value of the *power* property changes. The log entries appear as the string value of the *power* property changes.
But the `ngOnChanges` does not catch changes to `hero.name` But the `ngOnChanges` does not catch changes to `hero.name`
That's surprising at first. That's surprising at first.
@ -438,7 +439,7 @@ figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck") img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
:marked :marked
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost. While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency&mdash;after _every_ This hook is called with enormous frequency&mdash;after _every_
change detection cycle no matter where the change occurred. change detection cycle no matter where the change occurred.
It's called over twenty times in this example before the user can do anything. It's called over twenty times in this example before the user can do anything.
@ -480,7 +481,7 @@ a#wait-a-tick
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!). Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).
block tick-methods block tick-methods
:marked :marked
The `LoggerService.tick_then()` postpones the log update The `LoggerService.tick_then()` postpones the log update
for one turn of the browser's JavaScript cycle and that's just long enough. for one turn of the browser's JavaScript cycle and that's just long enough.
:marked :marked
@ -497,7 +498,7 @@ a#aftercontent
## AfterContent ## AfterContent
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls
*after* Angular projects external content into the component. *after* Angular projects external content into the component.
a#content-projection a#content-projection
:marked :marked
### Content projection ### Content projection
@ -529,13 +530,13 @@ figure.image-display
:marked :marked
.l-sub-section .l-sub-section
:marked :marked
The telltale signs of *content projection* are twofold: The telltale signs of *content projection* are twofold:
- HTML between component element tags. - HTML between component element tags.
- The presence of `<ng-content>` tags in the component's template. - The presence of `<ng-content>` tags in the component's template.
a#aftercontent-hooks a#aftercontent-hooks
:marked :marked
### AfterContent hooks ### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks. *AfterContent* hooks are similar to the *AfterView* hooks.
The key difference is in the child component. The key difference is in the child component.
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags * The *AfterView* hooks concern `ViewChildren`, the child components whose element tags

View File

@ -3,7 +3,7 @@ block includes
// TODO // TODO
Images Images
:marked :marked
**NgModules** help organize an application into cohesive blocks of functionality. **NgModules** help organize an application into cohesive blocks of functionality.
<!-- CF: "app" and "application" are used interchangeably throughout this page. <!-- CF: "app" and "application" are used interchangeably throughout this page.
@ -23,12 +23,14 @@ block includes
This page covers NgModules in greater depth. This page covers NgModules in greater depth.
## Table of Contents ## Table of Contents
<!-- CF: The titling for tables of contents in the advanced chapters is inconsistent:
<!-- CF: The titling for tables of contents in the advanced chapters is inconsistent:
- some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2 - some are titled "Contents" while others are titled "Table of Contents" (should probably be sentence case as it's an H2
- some headings are H2, some are H3 - some headings are H2, some are H3
- some pages don't have tables of contents - some pages don't have tables of contents
I didn't make changes here as I'm not sure what the correct style is. I didn't make changes here as I'm not sure what the correct style is.
--> -->
* [Angular modularity](#angular-modularity "Add structure to the app with NgModule") * [Angular modularity](#angular-modularity "Add structure to the app with NgModule")
* [The application root module](#root-module "The startup module that every app requires") * [The application root module](#root-module "The startup module that every app requires")
* [Bootstrap](#bootstrap "Launch the app in a browser with the root module as the entry point") the root module * [Bootstrap](#bootstrap "Launch the app in a browser with the root module as the entry point") the root module
@ -36,7 +38,7 @@ block includes
* [Providers](#providers "Extend the app with additional services") * [Providers](#providers "Extend the app with additional services")
* [Imports](#imports "Import components, directives, and pipes for use in component templates") * [Imports](#imports "Import components, directives, and pipes for use in component templates")
* [Resolve conflicts](#resolve-conflicts "When two directives have the same selector") * [Resolve conflicts](#resolve-conflicts "When two directives have the same selector")
<!-- CF: See my comment in the "Resolve diretive conflicts" section below proposing renaming or reorganizing that section. --> <!-- CF: See my comment in the "Resolve diretive conflicts" section below proposing renaming or reorganizing that section. -->
* [Feature modules](#feature-modules "Partition the app into feature modules") * [Feature modules](#feature-modules "Partition the app into feature modules")
* [Lazy loaded modules](#lazy-load "Load modules asynchronously") with the router * [Lazy loaded modules](#lazy-load "Load modules asynchronously") with the router
* [Shared modules](#shared-module "Create modules for commonly used components, directives, and pipes") * [Shared modules](#shared-module "Create modules for commonly used components, directives, and pipes")
@ -308,7 +310,7 @@ a#imports
You can write Angular form components in You can write Angular form components in
template-driven or template-driven or
[reactive](../cookbook/dynamic-form.html) style. [reactive](../cookbook/dynamic-form.html) style.
<!-- CF: this link goes to a page titled "Dynamic Forms". Should the link text be "dynamic" instead of "reactive"? --> <!-- CF: this link goes to a page titled "Dynamic Forms". Should the link text be "dynamic" instead of "reactive"? -->
The following sample imports the `FormsModule` from `@angular/forms` because The following sample imports the `FormsModule` from `@angular/forms` because
the `ContactComponent` is written in _template-driven_ style. the `ContactComponent` is written in _template-driven_ style.
@ -366,10 +368,10 @@ a#imports
.alert.is-critical .alert.is-critical
:marked :marked
*Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to *Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to
the `AppModule` metadata's declarations. the `AppModule` metadata's declarations.
These directives belong to the `FormsModule`. These directives belong to the `FormsModule`.
Components, directives, and pipes belong to _one module only_. Components, directives, and pipes belong to _one module only_.
*Never re-declare classes that belong to another module.* *Never re-declare classes that belong to another module.*
@ -391,7 +393,7 @@ a#import-name-conflict
+makeExample('ngmodule/ts/src/app/app.module.1b.ts', 'import-alias')(format=".") +makeExample('ngmodule/ts/src/app/app.module.1b.ts', 'import-alias')(format=".")
:marked :marked
This solves the immediate issue of referencing both directive _types_ in the same file but This solves the immediate issue of referencing both directive _types_ in the same file but
leaves another issue unresolved. leaves another issue unresolved.
You'll learn more about that issue later in this page, in [Resolve directive conflicts](#resolve-conflicts). You'll learn more about that issue later in this page, in [Resolve directive conflicts](#resolve-conflicts).
:marked :marked
@ -464,7 +466,7 @@ a#resolve-conflicts
:marked :marked
## Resolve directive conflicts ## Resolve directive conflicts
<!-- CF: This section describes directive conflicts in detail, but doesn't describe how to resolve them. <!-- CF: This section describes directive conflicts in detail, but doesn't describe how to resolve them.
This section seems like more of an introduction to the next section, "Feature modules". This section seems like more of an introduction to the next section, "Feature modules".
Consider moving this section to be a child section of "Feature modules", or striking "Resolve" from this title. --> Consider moving this section to be a child section of "Feature modules", or striking "Resolve" from this title. -->
An issue arose [earlier](#import-name-conflict) when you declared the contact's `HighlightDirective` because An issue arose [earlier](#import-name-conflict) when you declared the contact's `HighlightDirective` because
@ -543,7 +545,7 @@ a#feature-modules
While you can do everything within the root module, While you can do everything within the root module,
feature modules help you partition the app into areas of specific interest and purpose. feature modules help you partition the app into areas of specific interest and purpose.
<!-- CF: Is this paragraph just restating the previous paragraph? <!-- CF: Is this paragraph just restating the previous paragraph?
If so, I recommend removing it or merging the two --> If so, I recommend removing it or merging the two -->
A feature module collaborates with the root module and with other modules A feature module collaborates with the root module and with other modules
@ -582,7 +584,7 @@ a#feature-modules
Before `ContactComponent` can bind with `[(ngModel)]`, its `ContactModule` must import `FormsModule`. Before `ContactComponent` can bind with `[(ngModel)]`, its `ContactModule` must import `FormsModule`.
:marked :marked
You also replaced `BrowserModule` by `CommonModule`, for reasons explained in the You also replaced `BrowserModule` by `CommonModule`, for reasons explained in the
[Should I import BrowserModule or CommonModule?](../cookbook/ngmodule-faq.html#q-browser-vs-common-module) [Should I import BrowserModule or CommonModule?](../cookbook/ngmodule-faq.html#q-browser-vs-common-module)
section of the [NgModule FAQs](../cookbook/ngmodule-faq.html) page. section of the [NgModule FAQs](../cookbook/ngmodule-faq.html) page.
You _declare_ the contact component, directive, and pipe in the module `declarations`. You _declare_ the contact component, directive, and pipe in the module `declarations`.
@ -601,7 +603,7 @@ a#feature-modules
* Delete the contact import statements. * Delete the contact import statements.
* Delete the contact declarations and contact providers. * Delete the contact declarations and contact providers.
* Delete the `FormsModule` from the `imports` list (`AppComponent` doesn't need it). * Delete the `FormsModule` from the `imports` list (`AppComponent` doesn't need it).
Leave only the classes required at the application root level. Leave only the classes required at the application root level.
Then import the `ContactModule` so the app can continue to display the exported `ContactComponent`. Then import the `ContactModule` so the app can continue to display the exported `ContactComponent`.
@ -641,7 +643,7 @@ a#lazy-load
Their specifics aren't important to the story and this page doesn't discuss every line of code. Their specifics aren't important to the story and this page doesn't discuss every line of code.
.l-sub-section .l-sub-section
:marked :marked
Examine and download the complete source for this version from Examine and download the complete source for this version from
the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example> the <live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example>
:marked :marked
Some facets of the current application merit discussion are as follows: Some facets of the current application merit discussion are as follows:
@ -666,7 +668,7 @@ a#lazy-load
Some file names bear a `.3` extension that indicates Some file names bear a `.3` extension that indicates
a difference with prior or future versions. a difference with prior or future versions.
The significant differences will be explained in due course. The significant differences will be explained in due course.
<!-- CF: Can you be more specific here? Are the differences explained later in this page or in another page? --> <!-- CF: Can you be more specific here? Are the differences explained later in this page or in another page? -->
:marked :marked
The module still imports `ContactModule` so that its routes and components are mounted when the app starts. The module still imports `ContactModule` so that its routes and components are mounted when the app starts.
@ -782,7 +784,7 @@ a#hero-module
.file hero.service.ts .file hero.service.ts
.file highlight.directive.ts .file highlight.directive.ts
:marked :marked
This is the child routing scenario familiar to readers of the This is the child routing scenario familiar to readers of the
[Child routing component](router.html#child-routing-component) section of the [Child routing component](router.html#child-routing-component) section of the
[Routing & Navigation](router.html#child-routing-component) page. [Routing & Navigation](router.html#child-routing-component) page.
The `HeroComponent` is the feature's top component and routing host. The `HeroComponent` is the feature's top component and routing host.
@ -865,7 +867,7 @@ a#shared-module
`UserService` is an application-wide singleton. `UserService` is an application-wide singleton.
You don't want each module to have its own separate instance. You don't want each module to have its own separate instance.
Yet there is [a real danger](../cookbook/ngmodule-faq.html#q-why-it-is-bad) of that happening Yet there is [a real danger](../cookbook/ngmodule-faq.html#q-why-it-is-bad) of that happening
<!-- CF: This link goes to the top of the NgModule FAQs page. <!-- CF: This link goes to the top of the NgModule FAQs page.
It looks like it is supposed to go to a specific question/section within the page. --> It looks like it is supposed to go to a specific question/section within the page. -->
if the `SharedModule` provides the `UserService`. if the `SharedModule` provides the `UserService`.
@ -883,8 +885,8 @@ a#core-module
You didn't include them in the `SharedModule` for reasons just explained. You didn't include them in the `SharedModule` for reasons just explained.
Instead, gather them in a single `CoreModule` that you import once when the app starts Instead, gather them in a single `CoreModule` that you import once when the app starts
and never import anywhere else. and never import anywhere else.
Perform the following steps: Perform the following steps:
1. Create an `src/app/core` folder. 1. Create an `src/app/core` folder.
@ -927,8 +929,8 @@ a#core-module
Apps often have many singleton services like this sample's `UserService`. Apps often have many singleton services like this sample's `UserService`.
Each must be registered exactly once, in the app root injector, when the application starts. Each must be registered exactly once, in the app root injector, when the application starts.
While many components inject such services in their constructors&mdash;and While many components inject such services in their constructors&mdash;and
therefore require JavaScript `import` statements to import their symbols&mdash;no therefore require JavaScript `import` statements to import their symbols&mdash;no
other component or module should define or re-create the services themselves. other component or module should define or re-create the services themselves.
Their _providers_ aren't shared. Their _providers_ aren't shared.
@ -1026,7 +1028,7 @@ a#prevent-reimport
Only the root `AppModule` should import the `CoreModule`. Only the root `AppModule` should import the `CoreModule`.
[Bad things happen](../cookbook/ngmodule-faq.html#q-why-it-is-bad) if a lazy-loaded module imports it. [Bad things happen](../cookbook/ngmodule-faq.html#q-why-it-is-bad) if a lazy-loaded module imports it.
<!-- CF: Again, this link goes to the top of the NgModule FAQs page. <!-- CF: Again, this link goes to the top of the NgModule FAQs page.
It looks like it is supposed to go to a specific question/section within the page. --> It looks like it is supposed to go to a specific question/section within the page. -->
You could hope that no developer makes that mistake. You could hope that no developer makes that mistake.

View File

@ -18,6 +18,7 @@ block includes
The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs. The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs.
# Contents # Contents
* [Demos](#demos) * [Demos](#demos)
* [Providing HTTP Services](#http-providers) * [Providing HTTP Services](#http-providers)
* [The Tour of Heroes *HTTP* client demo](#http-client) * [The Tour of Heroes *HTTP* client demo](#http-client)
@ -224,13 +225,13 @@ a#HeroService
:marked :marked
## RxJS library ## RxJS library
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS Reactive Extensions">RxJS</a> <a href="http://reactivex.io/rxjs" target="_blank" title="RxJS Reactive Extensions">RxJS</a>
is a third party library, endorsed by Angular, that implements the is a third party library, endorsed by Angular, that implements the
<a href="https://www.youtube.com/watch?v=VLGCCpOWFFw" target="_blank" title="Video: Rob Wormald on Observables"><b>asynchronous Observable</b></a> pattern. <a href="https://www.youtube.com/watch?v=VLGCCpOWFFw" target="_blank" title="Video: Rob Wormald on Observables"><b>asynchronous Observable</b></a> pattern.
All of the Developer Guide samples have installed the RxJS npm package All of the Developer Guide samples have installed the RxJS npm package
because Observables are used widely in Angular applications. because Observables are used widely in Angular applications.
_This_ app needs it when working with the HTTP client. _This_ app needs it when working with the HTTP client.
But you must take a critical extra step to make RxJS Observables usable: But you must take a critical extra step to make RxJS Observables usable:
_you must import the RxJS operators individually_. _you must import the RxJS operators individually_.
### Enable RxJS operators ### Enable RxJS operators
@ -307,7 +308,7 @@ a#error-handling
and do something with them. One way to handle errors is to pass an error message and do something with them. One way to handle errors is to pass an error message
back to the component for presentation to the user, back to the component for presentation to the user,
but only if it says something that the user can understand and act upon. but only if it says something that the user can understand and act upon.
This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error. This simple app conveys that idea, albeit imperfectly, in the way it handles a `getHeroes` error.
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'error-handling', 'src/app/toh/hero.service.ts (excerpt)')(format=".") +makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'error-handling', 'src/app/toh/hero.service.ts (excerpt)')(format=".")
@ -315,7 +316,7 @@ a#error-handling
block error-handling block error-handling
:marked :marked
The `catch()` operator passes the error object from `http` to the `handleError()` method. The `catch()` operator passes the error object from `http` to the `handleError()` method.
The `handleError` method transforms the error into a developer-friendly message, The `handleError` method transforms the error into a developer-friendly message,
logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`. logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`.
a#subscribe a#subscribe
@ -421,8 +422,8 @@ block hero-list-comp-add-hero
You can follow the Promise `then(this.extractData).catch(this.handleError)` pattern as in You can follow the Promise `then(this.extractData).catch(this.handleError)` pattern as in
this example. this example.
Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the
first *success* parameter and its `catch` callback to the second *fail* parameter first *success* parameter and its `catch` callback to the second *fail* parameter
in this pattern: `.toPromise(this.extractData, this.handleError)`. in this pattern: `.toPromise(this.extractData, this.handleError)`.
The `errorHandler` forwards an error message as a failed `Promise` instead of a failed `Observable`. The `errorHandler` forwards an error message as a failed `Promise` instead of a failed `Observable`.
@ -442,15 +443,15 @@ block hero-list-comp-add-hero
:marked :marked
The less obvious but critical difference is that these two methods return very different results. The less obvious but critical difference is that these two methods return very different results.
The Promise-based `then()` returns another Promise. You can keep chaining The Promise-based `then()` returns another Promise. You can keep chaining
more `then()` and `catch()` calls, getting a new promise each time. more `then()` and `catch()` calls, getting a new promise each time.
The `subscribe()` method returns a `Subscription`. A `Subscription` is not another `Observable`. The `subscribe()` method returns a `Subscription`. A `Subscription` is not another `Observable`.
It's the end of the line for Observables. You can't call `map()` on it or call `subscribe()` again. It's the end of the line for Observables. You can't call `map()` on it or call `subscribe()` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`. The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
To understand the implications and consequences of subscriptions, To understand the implications and consequences of subscriptions,
watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE) watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises). or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
h2#cors Cross-Origin Requests: Wikipedia example h2#cors Cross-Origin Requests: Wikipedia example
@ -494,7 +495,7 @@ block wikipedia-jsonp+
+makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts',null,'src/app/wiki/wikipedia.service.ts') +makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts',null,'src/app/wiki/wikipedia.service.ts')
:marked :marked
The constructor expects Angular to inject its `Jsonp` service, which The constructor expects Angular to inject its `Jsonp` service, which
is available because `JsonpModule` is in the root `@NgModule` `imports` array is available because `JsonpModule` is in the root `@NgModule` `imports` array
in `app.module.ts`. in `app.module.ts`.
@ -521,7 +522,7 @@ block wikipedia-jsonp+
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object. This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
+makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts','call-jsonp','src/app/wiki/wikipedia.service.ts (call jsonp)')(format=".") +makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts','call-jsonp','src/app/wiki/wikipedia.service.ts (call jsonp)')(format=".")
:marked :marked
`Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request `Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request
to the server. to the server.
<a id="wikicomponent"></a> <a id="wikicomponent"></a>
@ -535,15 +536,15 @@ block wikipedia-jsonp+
The template presents an `<input>` element *search box* to gather search terms from the user, The template presents an `<input>` element *search box* to gather search terms from the user,
and calls a `search(term)` method after each `keyup` event. and calls a `search(term)` method after each `keyup` event.
The component's `search(term)` method delegates to the `WikipediaService`, which returns an The component's `search(term)` method delegates to the `WikipediaService`, which returns an
Observable !{_array} of string results (`Observable<string[]>`). Observable !{_array} of string results (`Observable<string[]>`).
Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`, Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`,
the app forwards the Observable result to the template (via `items`) where the `async` pipe the app forwards the Observable result to the template (via `items`) where the `async` pipe
in the `ngFor` handles the subscription. Read more about [async pipes](pipes.html#async-pipe) in the `ngFor` handles the subscription. Read more about [async pipes](pipes.html#async-pipe)
in the [Pipes](pipes.html) page. in the [Pipes](pipes.html) page.
.l-sub-section .l-sub-section
:marked :marked
The [async pipe](pipes.html#async-pipe) is a good choice in read-only components The [async pipe](pipes.html#async-pipe) is a good choice in read-only components
where the component has no need to interact with the data. where the component has no need to interact with the data.
`HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list. `HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list.
@ -576,7 +577,7 @@ block wikipedia-jsonp+
Which response arrives first? It's unpredictable. Which response arrives first? It's unpredictable.
When there are multiple requests in-flight, the app should present the responses When there are multiple requests in-flight, the app should present the responses
in the original request order. in the original request order.
In this example, the app must always display the results for the *http* search In this example, the app must always display the results for the *http* search
no matter which response arrives first. no matter which response arrives first.
@ -591,14 +592,14 @@ block wikipedia-jsonp+
+makeTabs( +makeTabs(
`server-communication/ts/src/app/wiki/wiki-smart.component.ts, `server-communication/ts/src/app/wiki/wiki-smart.component.ts,
server-communication/ts/src/app/wiki/wiki.component.ts`, server-communication/ts/src/app/wiki/wiki.component.ts`,
null, null,
`src/app/wiki/wiki-smart.component.ts, `src/app/wiki/wiki-smart.component.ts,
src/app/wiki/wiki.component.ts` src/app/wiki/wiki.component.ts`
) )
:marked :marked
While the templates are virtually identical, While the templates are virtually identical,
there's a lot more RxJS in the "smart" version, there's a lot more RxJS in the "smart" version,
starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators, starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators,
imported as [described above](#rxjs-library). imported as [described above](#rxjs-library).
@ -616,12 +617,12 @@ block wikipedia-jsonp+
The `search()` method adds each new search box value to that stream via the subject's `next()` method. The `search()` method adds each new search box value to that stream via the subject's `next()` method.
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'subject')(format='.') +makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
a#listen-for-search-terms a#listen-for-search-terms
:marked :marked
### Listen for search terms ### Listen for search terms
The `WikiSmartComponent` listens to the *stream of search terms* and The `WikiSmartComponent` listens to the *stream of search terms* and
processes that stream _before_ calling the service. processes that stream _before_ calling the service.
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.') +makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
:marked :marked
@ -650,15 +651,15 @@ a#xsrf
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
a different web page with malignant code that secretly sends a malicious request to your application's web server. a different web page with malignant code that secretly sends a malicious request to your application's web server.
The server and client application must work together to thwart this attack. The server and client application must work together to thwart this attack.
Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests. Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests.
The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly
generated authentication token in a cookie named `XSRF-TOKEN`. generated authentication token in a cookie named `XSRF-TOKEN`.
The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests. The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests.
The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match. The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match.
See the [XSRF topic on the Security page](security.html#xsrf) for more information about XSRF and Angular's `XSRFStrategy` counter measures. See the [XSRF topic on the Security page](security.html#xsrf) for more information about XSRF and Angular's `XSRFStrategy` counter measures.
a#override-default-request-options a#override-default-request-options
@ -667,7 +668,7 @@ a#override-default-request-options
## Override default request headers (and other request options) ## Override default request headers (and other request options)
Request options (such as headers) are merged into the Request options (such as headers) are merged into the
[default _RequestOptions_](https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html "API: BaseRequestOptions") [default _RequestOptions_](https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html "API: BaseRequestOptions")
before the request is processed. before the request is processed.
The `HttpModule` provides these default options via the `RequestOptions` token. The `HttpModule` provides these default options via the `RequestOptions` token.
@ -692,10 +693,10 @@ a#override-default-request-options
:marked :marked
You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab. You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab.
If you're short-circuiting the server call with something like the [_in-memory web api_](#in-mem-web-api), If you're short-circuiting the server call with something like the [_in-memory web api_](#in-mem-web-api),
try commenting-out the `create` header option, try commenting-out the `create` header option,
set a breakpoint on the POST call, and step through the request processing set a breakpoint on the POST call, and step through the request processing
to verify the header is there. to verify the header is there.
Individual requests options, like this one, take precedence over the default `RequestOptions`. Individual requests options, like this one, take precedence over the default `RequestOptions`.
It might be wise to keep the `create` request header setting for extra safety. It might be wise to keep the `create` request header setting for extra safety.
@ -720,13 +721,13 @@ a#in-mem-web-api
:marked :marked
The *get heroes* scenario would work, The *get heroes* scenario would work,
but since the app can't save changes to a JSON file, it needs a web API server. but since the app can't save changes to a JSON file, it needs a web API server.
Because there isn't a real server for this demo, Because there isn't a real server for this demo,
it substitutes the Angular _in-memory web api_ simulator for the actual XHR backend service. it substitutes the Angular _in-memory web api_ simulator for the actual XHR backend service.
.l-sub-section .l-sub-section
:marked :marked
The in-memory web api is not part of Angular _proper_. The in-memory web api is not part of Angular _proper_.
It's an optional service in its own It's an optional service in its own
<a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API"><i>angular-in-memory-web-api</i></a> <a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API"><i>angular-in-memory-web-api</i></a>
library installed with npm (see `package.json`). library installed with npm (see `package.json`).
@ -770,7 +771,7 @@ block redirect-to-web-api
+makeExcerpt('src/app/app.module.ts') +makeExcerpt('src/app/app.module.ts')
.alert.is-important .alert.is-important
:marked :marked
Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that
the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others. the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others.
:marked :marked
See the full source code in the <live-example></live-example>. See the full source code in the <live-example></live-example>.

View File

@ -14,6 +14,7 @@ block includes
a#top a#top
:marked :marked
# Contents # Contents
* [Live examples](#live-examples "Live examples of the tests in this guide") * [Live examples](#live-examples "Live examples of the tests in this guide")
<br><br> <br><br>
* [Introduction to Angular testing](#testing-intro) * [Introduction to Angular testing](#testing-intro)