docs(*): blank lines before lists are good (#3474)
This commit is contained in:
parent
22918b40b5
commit
93d3db1fd4
|
@ -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
|
@ -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—a set for each validated property with
|
Next, the component needs some error messages of course—a set for each validated property with
|
||||||
one message per validation rule:
|
one message per validation rule:
|
||||||
+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—`FormsModule` and
|
Angular has two different forms modules—`FormsModule` and
|
||||||
`ReactiveFormsModule`—that correspond with the
|
`ReactiveFormsModule`—that correspond with the
|
||||||
two approaches to form development. Both modules come
|
two approaches to form development. Both modules come
|
||||||
from the same `@angular/forms` library package.
|
from the same `@angular/forms` library package.
|
||||||
|
|
||||||
You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
|
You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
|
||||||
|
@ -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`—the instance in the form with
|
the `ForbiddenValidatorDirective`—the instance in the form with
|
||||||
its `forbiddenName` property bound to “bob". If you were to replace
|
its `forbiddenName` property bound to “bob". If you were to replace
|
||||||
`useExisting` with `useClass`, then you’d be registering a new class instance, one that
|
`useExisting` with `useClass`, then you’d be registering a new class instance, one that
|
||||||
doesn’t have a `forbiddenName`.
|
doesn’t have a `forbiddenName`.
|
||||||
|
|
||||||
To see this in action, run the example and then type “bob” in the name of Hero Form 2.
|
To see this in action, run the example and then type “bob” in the name of Hero Form 2.
|
||||||
Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
|
Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
|
||||||
This time, when you type “bob”, there's no "bob" error message.
|
This time, when you type “bob”, there's no "bob" error message.
|
||||||
|
|
||||||
: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—factors that tend to diminish test code
|
skill—factors that tend to diminish test code
|
||||||
coverage and quality.
|
coverage and quality.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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_ — nest each constructor function in a sub-array.
|
* _plain ES6_ — nest each constructor function in a sub-array.
|
||||||
|
|
||||||
* _ES5_ — simply list the constructor functions.
|
* _ES5_ — 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_ — each item is a new instance of `Inject(token)` in a sub-array.
|
* _plain ES6_ — each item is a new instance of `Inject(token)` in a sub-array.
|
||||||
|
|
||||||
* _ES5_ — simply list the string tokens.
|
* _ES5_ — simply list the string tokens.
|
||||||
|
|
||||||
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
|
When writing with _ES5 DSL_, set the `Class.constructor` property to a function definition
|
||||||
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
|
array as before. Create a new instance of `ng.core.Inject(token)` for each parameter.
|
||||||
|
|
||||||
+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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 — without any changes to the source code.
|
and performs AOT compilation — 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 [_<base href="..."/>_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
|
The HTML [_<base href="..."/>_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
|
||||||
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
|
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
|
||||||
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
|
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
|
||||||
into a server request for `my/app/some/place/foo.jpg`.
|
into a server request for `my/app/some/place/foo.jpg`.
|
||||||
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
|
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
|
||||||
|
|
||||||
|
@ -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 —
|
or merely refreshing the browser while on the hero detail page —
|
||||||
all of these actions are handled by the browser itself, _outside_ the running application.
|
all of these actions are handled by the browser itself, _outside_ the running application.
|
||||||
The browser makes a direct request to the server for that URL, bypassing the router.
|
The browser makes a direct request to the server for that URL, bypassing the router.
|
||||||
|
|
||||||
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
|
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
|
||||||
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
|
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
|
||||||
configured to return `index.html` instead.
|
configured to return `index.html` instead.
|
||||||
|
|
||||||
#### Fallback configuration examples
|
#### Fallback configuration examples
|
||||||
|
|
||||||
There is no single configuration that works for every server.
|
There is no single configuration that works for every server.
|
||||||
The following sections describe configurations for some of the most popular servers.
|
The following sections describe configurations for some of the most popular servers.
|
||||||
The list is by no means exhaustive, but should provide you with a good starting point.
|
The list is by no means exhaustive, but should provide you with a good starting point.
|
||||||
|
|
||||||
#### Development servers
|
#### Development servers
|
||||||
|
|
||||||
- [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>.
|
||||||
|
|
||||||
|
|
|
@ -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—after _every_
|
This hook is called with enormous frequency—after _every_
|
||||||
change detection cycle no matter where the change occurred.
|
change detection cycle no matter where the change occurred.
|
||||||
It's called over twenty times in this example before the user can do anything.
|
It's called over twenty times in this example before the user can do anything.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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`—or the `FORMS_DIRECTIVES`—to
|
*Do not* add `NgModel`—or the `FORMS_DIRECTIVES`—to
|
||||||
the `AppModule` metadata's declarations.
|
the `AppModule` metadata's declarations.
|
||||||
These directives belong to the `FormsModule`.
|
These directives belong to the `FormsModule`.
|
||||||
|
|
||||||
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—and
|
While many components inject such services in their constructors—and
|
||||||
therefore require JavaScript `import` statements to import their symbols—no
|
therefore require JavaScript `import` statements to import their symbols—no
|
||||||
other component or module should define or re-create the services themselves.
|
other component or module should define or re-create the services themselves.
|
||||||
Their _providers_ aren't shared.
|
Their _providers_ aren't shared.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
|
@ -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>.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue