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

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

View File

@ -7,6 +7,7 @@ include ../_util-fns
a#toc
:marked
# Contents
- [Overview](overview)
- [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit)
- [Why do AOT compilation?](#why-aot)
@ -282,8 +283,8 @@ a#rollup-plugins
Luckily, there is a Rollup plugin that modifies _RxJs_
to use the ES `import` and `export` statements that Rollup requires.
Rollup then preserves the parts of `RxJS` referenced by the application
in the final bundle. Using it is straigthforward. Add the following to
Rollup then preserves the parts of `RxJS` referenced by the application
in the final bundle. Using it is straigthforward. Add the following to
the `plugins` !{_array} in `rollup-config.js`:
+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*
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}:
+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
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.
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.
Develop as usual.
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`.
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`.
Now you can develop JIT and AOT, side-by-side.

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,11 @@ a#top
:marked
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.
.l-sub-section
: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.
a#toc
@ -40,20 +40,20 @@ a#template1
:marked
## 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.
You add Angular form directives (mostly directives beginning `ng...`) to help
Angular construct a corresponding internal control model that implements form functionality.
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.
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.
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.
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='.')
@ -66,9 +66,9 @@ a#template1
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 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_
to check for control states such as `valid` and `dirty`.
@ -102,7 +102,7 @@ a#why-check
+makeTabs(
`cb-form-validation/ts/src/app/template/hero-form-template1.component.html,
cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`,
'',
'',
`template/hero-form-template1.component.html,
template/hero-form-template1.component.ts`)
@ -111,24 +111,24 @@ a#template2
:marked
## 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:
* 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.
* 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.
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.
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:
+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`,
'name-with-error-msg, name-with-error-msg',
`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.
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.
- 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.
a#component-class
a#component-class
:marked
### Component class
The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code
necessary in Template 2's component class to acquire the Angular
The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code
necessary in Template 2's component class to acquire the Angular
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.
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:
+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
Some observations:
- Angular `@ViewChild` queries for a template variable when you pass it
- Angular `@ViewChild` queries for a template variable when you pass it
the name of that variable as a string (`'heroForm'` in this case).
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes.
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
when anything changes in the view.
That's the right time to see if there's a new `heroForm` object.
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
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='.')
: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 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:
- Clears the prior error message, if any.
- Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty")
- Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
Next, the component needs some error messages of course&mdash;a set for each validated property with
Next, the component needs some error messages of course&mdash;a set for each validated property with
one message per validation rule:
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.')
:marked
@ -207,25 +207,25 @@ a#component-class
a#improvement
:marked
### 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.
Consider what happens as the number of validated
Consider what happens as the number of validated
fields and rules increases.
In general, HTML is harder to read and maintain than code.
The initial template was already large and threatening to get rapidly worse
In general, HTML is harder to read and maintain than code.
The initial template was already large and threatening to get rapidly worse
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.
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
and one line per validation message.
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.
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
### _FormModule_ and template-driven forms
Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come
Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come
from the same `@angular/forms` library package.
You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
@ -245,8 +245,8 @@ a#formmodule
.l-sub-section
:marked
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.
.l-main-section
@ -254,11 +254,11 @@ a#reactive
:marked
## 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`.
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
and `form...` directives from the Angular `ReactiveFormsModule`.
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:
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts','','src/app/reactive/hero-form-reactive.module.ts')(format='.')
: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.
a#reactive-component-template
@ -287,7 +287,7 @@ a#reactive-component-template
### Component 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.
+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.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
+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`,
'name-with-error-msg, name-with-error-msg',
`hero-form-reactive.component.html (name #3),
@ -303,16 +303,16 @@ a#reactive-component-template
:marked
Key changes are:
- The validation attributes are gone (except `required`) because
- The validation attributes are gone (except `required`) because
validating happens in code.
- `required` remains, not for validation purposes (that's in the code),
- `required` remains, not for validation purposes (that's in the code),
but rather for css styling and accessibility.
.l-sub-section
:marked
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
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
purpose of correlating the input with the Angular form control.
- The two-way `[(ngModel)]` binding is gone.
- The two-way `[(ngModel)]` binding is gone.
The reactive approach does not use data binding to move data into and out of the form controls.
That's all in code.
@ -332,10 +332,10 @@ a#reactive-component-class
:marked
### 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.
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.
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.
:marked
- The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanges` event and calls it immediately
Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanges` event and calls it immediately
to set error messages for the new control model.
a#formbuilder
:marked
#### _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 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.
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).
.l-sub-section
.l-sub-section
: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
:marked
#### Committing hero value changes
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.
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.
+makeTabs(
`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`,
'',
`reactive/hero-form-reactive.component.ts (#3),
@ -422,7 +422,7 @@ a#committing-changes
a#custom-validation
:marked
## 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
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
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".
Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
The `forbiddenNameValidator` factory returns the configured validator function.
That function takes an Angular control object and returns _either_
null if the control value is valid _or_ a validation error object.
@ -445,7 +445,7 @@ a#custom-validation
a#custom-validation-directive
:marked
### 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.
+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
@ -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='.')
:marked
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
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='.')
@ -465,22 +465,22 @@ a#custom-validation-directive
:marked
.l-sub-section
:marked
If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of
the `ForbiddenValidatorDirective`&mdash;the instance in the form with
its `forbiddenName` property bound to “bob". If you were to replace
`useExisting` with `useClass`, then youd be registering a new class instance, one that
If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of
the `ForbiddenValidatorDirective`&mdash;the instance in the form with
its `forbiddenName` property bound to “bob". If you were to replace
`useExisting` with `useClass`, then youd be registering a new class instance, one that
doesnt have a `forbiddenName`.
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.
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.
This time, when you type “bob”, there's no "bob" error message.
:marked
.l-sub-section
:marked
For more information on attaching behavior to elements,
For more information on attaching behavior to elements,
see [Attribute Directives](../guide/attribute-directives.html).
.l-main-section
@ -497,11 +497,11 @@ a#testing
They do not require the `Angular TestBed` or asynchronous testing practices.
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.
You must use the `Angular TestBed` to create component test instances,
write asynchronous tests, and interact with the DOM.
While not difficult, this takes more time, work and
skill&mdash;factors that tend to diminish test code
While not difficult, this takes more time, work and
skill&mdash;factors that tend to diminish test code
coverage and quality.

View File

@ -14,6 +14,7 @@ block includes
:marked
Declarations
* [What classes should I add to _declarations_?](#q-what-to-declare)
* [What is a _declarable_?](#q-declarable)
* [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)
Imports
* [What should I import?](#q-what-to-import)
* [Should I import _BrowserModule_ or _CommonModule_?](#q-browser-vs-common-module)
* [What if I import the same module twice?](#q-reimport)
Exports
* [What should I export?](#q-what-to-export)
* [What should I *not* export?](#q-what-not-to-export)
* [Can I re-export imported classes and modules?](#q-re-export)
* [What is the _forRoot_ method?](#q-for-root)
Service Providers
* [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)
* [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)
Entry Components
* [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)
* [When do I add components to _entryComponents_?](#q-when-entry-components)
* [Why does Angular need _entryComponents_?](#q-why-entry-components)
General
* [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)
* [How does Angular find components, directives, and pipes in a template?](#q-template-reference)

View File

@ -5,8 +5,8 @@ include ../../../../_includes/_util-fns
in JavaScript. Translating from one language to the other is mostly a
matter of changing the way you organize your code and access Angular APIs.
_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_.
_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_.
This cookbook contains recipes for translating _TypeScript_
code examples to _ES6_ and to _ES5_ so that JavaScript developers
can read and write Angular apps in their preferred dialect.
@ -15,16 +15,16 @@ a#toc
:marked
## Table of contents
[_TypeScript_ to _ES6_ to _ES5_](#from-ts)<br>
[Modularity: imports and exports](#modularity)<br>
[Classes and Class Metadata](#class-metadata)<br>
[_ES5_ DSL](#dsl)<br>
[Interfaces](#interfaces)<br>
[Input and Output Metadata](#io-decorators)<br>
[Dependency Injection](#dependency-injection)<br>
[Host Binding](#host-binding)<br>
[View and Child Decorators](#view-child-decorators)<br>
[AOT compilation in _TypeScript_ Only](#aot)<br>
* [_TypeScript_ to _ES6_ to _ES5_](#from-ts)<br>
* [Modularity: imports and exports](#modularity)<br>
* [Classes and Class Metadata](#class-metadata)<br>
* [_ES5_ DSL](#dsl)<br>
* [Interfaces](#interfaces)<br>
* [Input and Output Metadata](#io-decorators)<br>
* [Dependency Injection](#dependency-injection)<br>
* [Host Binding](#host-binding)<br>
* [View and Child Decorators](#view-child-decorators)<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>
code shown in this cookbook.**
@ -33,8 +33,8 @@ a#from-ts
.l-main-section
:marked
## _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>.
_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.
@ -44,29 +44,29 @@ a#from-ts
* _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_")
* _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)
such as `public` and `private`.
Remove most of the
[type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html),
Remove most of the
[type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html),
such as `:string` and `:boolean`
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)
and the remaining types.
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`
statements and `class` declarations.
statements and `class` declarations.
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.
Transpile with [Babel](https://babeljs.io/) using the `es2015` preset.
To use decorators and annotations with Babel, install 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.
Transpile with [Babel](https://babeljs.io/) using the `es2015` preset.
To use decorators and annotations with Babel, install the
[`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well.
a#modularity
.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 _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:
+makeTabs(`
@ -100,23 +100,23 @@ a#modularity
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.
_ES5_ lacks native support for modules.
In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`.
_ES5_ lacks native support for modules.
In an Angular _ES5_ application, you load each file manually by adding a `<script>` tag to `index.html`.
.alert.is-important
: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.
: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`.
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`.
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)
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.
+makeTabs(`
@ -170,21 +170,21 @@ a#class-metadata
Most Angular _TypeScript_ and _ES6_ code is written as classes.
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.
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!***
Look for types in _TypeScript_ property declarations.
In general it is better to initialize such properties with default values because
many browser JavaScript engines can generate more performant code.
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.
_ES5_ JavaScript has no classes.
_ES5_ JavaScript has no classes.
Use the constructor function pattern instead, adding methods to the prototype.
+makeTabs(`
@ -203,15 +203,15 @@ a#class-metadata
:marked
### Metadata
When writing in _TypeScript_ or _ES6-with-decorators_,
provide configuration and metadata by adorning a class with one or more *decorators*.
When writing in _TypeScript_ or _ES6-with-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
[`@Component`](../api/core/index/Component-decorator.html) decorator function whose
argument is an object literal with metadata properties.
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.
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:
@ -256,8 +256,8 @@ a#dsl
:marked
## _ES5_ DSL
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,
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,
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_).
@ -283,8 +283,8 @@ a#dsl
header Name the constructor
:marked
A **named** constructor displays clearly in the console log
if the component throws a runtime error.
An **unnamed** constructor displays as an anonymous function (e.g., `class0`)
if the component throws a runtime error.
An **unnamed** constructor displays as an anonymous function (e.g., `class0`)
which is impossible to find in the source code.
:marked
@ -306,8 +306,8 @@ a#dsl
:marked
### DSL for other classes
There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`:
There are similar DSLs for other decorated classes.
You can define a directive with `ng.core.Directive`:
code-example.
app.MyDirective = ng.core.Directive({
@ -317,7 +317,7 @@ code-example.
});
:marked
and a pipe with `ng.core.Pipe`:
code-example.
code-example.
app.MyPipe = ng.core.Pipe({
name: 'myPipe'
}).Class({
@ -330,7 +330,7 @@ a#interfaces
## Interfaces
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
should implement the `OnInit` interface.
@ -351,7 +351,7 @@ a#interfaces
ES6 JavaScript,
ES5 JavaScript,
ES5 JavaScript with DSL
`)
`)
a#io-decorators
.l-main-section
@ -361,15 +361,15 @@ a#io-decorators
### Input and Output 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.
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.
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
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
combined in the metadata `inputs` and `outputs` _arrays_.
@ -388,21 +388,21 @@ a#io-decorators
ES5 JavaScript with DSL
`)
: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`).
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.
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.
In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata.
.l-main-section
:marked
## Dependency Injection
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_.
The developer must tell Angular what to inject into each parameter.
@ -410,26 +410,26 @@ a#io-decorators
### Injection by Class 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.
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.
_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.
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_.
The format of the `parameters` array varies:
* _plain ES6_ &mdash; nest each constructor function in a sub-array.
* _ES5_ &mdash; simply list the constructor functions.
When writing with _ES5 DSL_, set the `Class.constructor` property to
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.
+makeTabs(`
@ -451,20 +451,20 @@ a#io-decorators
### Injection with the @Inject decorator
Sometimes the dependency injection token isn't a class or constructor function.
In _TypeScript_ and _ES6-with-decorators_, you precede the class constructor parameter
by calling the `@Inject()` decorator with the injection token.
In the following example, the token is the string `'heroName'`.
The other JavaScript dialects add a `parameters` array to the class contructor function.
Each item constains a new instance of `Inject`:
* _plain ES6_ &mdash; each item is a new instance of `Inject(token)` in a sub-array.
* _ES5_ &mdash; simply list the string tokens.
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(`
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`.
In _TypeScript_ and _ES6-with-decorators_,
In _TypeScript_ and _ES6-with-decorators_,
you precede the constructor parameters with injection qualifiers such as:
* [`@Optional`](../api/core/index/Optional-decorator.html) sets the parameter to `null` if the service is missing
* [`@Attribute`](../api/core/index/Attribute-interface.html) to inject a host element attribute value
@ -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.
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
array as before. Use a nested array to define a parameter's complete injection specification.
@ -520,7 +520,7 @@ a#io-decorators
:marked
In the example above, there is no provider for the `'titlePrefix'` token.
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.
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
element to a component or directive.
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
host element events to component event handlers.
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:
* Each key follows regular Angular binding syntax: `[property]` for host bindings
or `(event)` for host listeners.
* Each value identifies the corresponding component property or method.
@ -566,7 +566,7 @@ a#host-binding
:marked
### Host Metadata
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_.
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
:marked
### View and Child Decorators
Several _property_ decorators query a component's nested view and content components.
.l-sub-section
:marked
_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;
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
The [`@ViewChild`](../api/core/index/ViewChild-decorator.html) and
[`@ViewChildren`](../api/core/index/ViewChildren-decorator.html) property decorators
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.
* 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
:marked
In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata
instead of the `@ViewChild` and `@ContentChild` property decorators.
In _TypeScript_ and _ES6-with-decorators_ you can also use the `queries` metadata
instead of the `@ViewChild` and `@ContentChild` property decorators.
a#aot
.l-main-section
:marked
## 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).
Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates
_TypeScript_ files as an intermediate result.

View File

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

View File

@ -1,11 +1,12 @@
include ../_util-fns
:marked
This page describes tools and techniques for deploy and optimize your Angular application.
a#toc
:marked
## Table of contents
* [Overview](#overview)
* [Simplest deployment possible](#dev-deploy)
* [Optimize for production](#optimize)
@ -19,7 +20,7 @@ a#toc
* [Enable production mode](#enable-prod-mode)
* [Lazy loading](#lazy-loading)
* [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)
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.
* [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of
[several optimization strategies](#optimize).
* [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of
[several optimization strategies](#optimize).
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.
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.
@ -49,20 +50,20 @@ a#overview
.l-main-section
a#dev-deploy
:marked
## Simplest deployment possible
## Simplest deployment possible
The simplest way to deploy the app is to publish it to a web server
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.
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.
1. If you're serving the app out of a subfolder,
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
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
`<base href="/my/app/">`.
Otherwise, leave it alone.
[More on this below](#base-tag).
@ -85,12 +86,12 @@ a#node-modules
:marked
### 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.
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.
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.
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=".")
: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`.
+makeExample('deployment/ts/src/index.html', 'systemjs-config', '')(format=".")
:marked
(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.
Modify `systemjs.config.server.js` as necessary to stay in sync with changes
@ -117,15 +118,15 @@ a#node-modules
:marked
In the standard SystemJS config, the `npm` path points to the `node_modules/`.
In this server config, it points to
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
In this server config, it points to
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
a site that hosts _npm packages_,
and loads them from the web directly.
There are other service providers that do the same thing.
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
you would copy to a library folder on the server.
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
you would copy to a library folder on the server.
Then change the config's `'npm'` path to point to that folder.
### Practice with an example
@ -140,7 +141,7 @@ a#node-modules
deployment/ts/src/app/app.component.ts,
deployment/ts/src/app/crisis-list.component.ts,
deployment/ts/src/app/hero-list.component.ts
`,
`,
null,
`index.html,
systemjs.config.server.js,
@ -161,9 +162,9 @@ a#node-modules
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.
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`
until you restored it).
@ -177,26 +178,26 @@ a#optimize
## Optimize for production
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,
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.
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 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.
The many requests and large payloads mean
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.
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.
- 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.
- Minification: removes excess whitespace, comments, and optional tokens.
- 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.
- 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.
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.
a#aot
@ -227,20 +228,20 @@ a#aot
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
and using [_rollup_](#rollup) for bundling, minification, uglification and tree shaking.
a#webpack
:marked
### Webpack (and AOT)
<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.
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.
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">
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,
and performs AOT compilation &mdash; without any changes to the source code.
@ -250,12 +251,12 @@ a#rollup
Any code that you don't call is _dead code_.
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.
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
<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.
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">
@ -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
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 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">
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.
a#angular-configuration
@ -301,10 +302,10 @@ a#angular-configuration
a#base-tag
:marked
### The `base` tag
The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
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`.
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
See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API: APP_BASE_HREF") alternative.
: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.
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`.
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
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=".")
@ -345,7 +346,7 @@ a#lazy-loading
You can dramatically reduce launch time by only loading the application modules that
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")
or by [_lazy loading_](router.html#asynchronous-routing "Lazy loading")
them on demand.
@ -356,14 +357,14 @@ a#lazy-loading
You've arranged to lazy load a module.
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`.
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.
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.
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)
automatically recognizes lazy loaded `NgModules` and creates separate bundles for them.
@ -372,7 +373,7 @@ a#server-configuration
.l-main-section
:marked
## Server configuration
This section covers changes you may have make to the server or to files deployed to the server.
a#fallback
@ -383,7 +384,7 @@ a#fallback
You don't need a server-side engine to dynamically compose application pages because
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.
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.
The Angular router interprets the URL and routes to that page and hero.
But clicking a link in an email, entering it in the browser address bar,
But clicking a link in an email, entering it in the browser address bar,
or merely refreshing the browser while on the hero detail page &mdash;
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.
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.
#### Fallback configuration examples
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.
#### Development servers
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
`historyApiFallback` entry in the dev server options as follows:
code-example().
@ -427,13 +428,13 @@ code-example().
:marked
#### Production servers
- [Apache](https://httpd.apache.org/): add a
- [Apache](https://httpd.apache.org/): add a
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
to the `.htaccess` file as show
to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
code-example(format=".").
RewriteEngine On
RewriteEngine On
# 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} -d
@ -443,7 +444,7 @@ code-example(format=".").
RewriteRule ^ /index.html
:marked
- [NGinx](http://nginx.org/): use `try_files`, as described in
- [NGinx](http://nginx.org/): use `try_files`, as described in
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
modified to serve `index.html`:
@ -470,16 +471,16 @@ code-example(format="." escape="html").
</system.webServer>
:marked
- [GitHub Pages](https://pages.github.com/): you can't
[directly configure](https://github.com/isaacs/github/issues/408)
the GitHub Pages server, but you can add a 404 page.
- [GitHub Pages](https://pages.github.com/): you can't
[directly configure](https://github.com/isaacs/github/issues/408)
the GitHub Pages server, but you can add a 404 page.
Copy `index.html` into `404.html`.
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
[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
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)
and to
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
@ -493,7 +494,7 @@ a#cors
.l-main-section
:marked
### Requesting services from a different server (CORS)
Angular developers may encounter a
<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).
@ -501,7 +502,7 @@ a#cors
Browsers forbid such requests unless the server permits them explicitly.
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
<a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>.

View File

@ -8,7 +8,7 @@ figure
:marked
A component has a lifecycle managed by Angular.
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.
@ -20,6 +20,7 @@ figure
+ifDocsFor('ts|js')
:marked
## Contents
* [Component lifecycle hooks overview](#hooks-overview)
* [Lifecycle sequence](#hooks-purpose-timing)
* [Interfaces are optional (technically)](#interface-optional)
@ -37,7 +38,7 @@ figure
* [Content projection](#content-projection)
* [AfterContent hooks](#aftercontent-hooks)
* [No unidirectional flow worries with _AfterContent_](#no-unidirectional-flow-worries)
:marked
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.
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:
+makeExample('lifecycle-hooks/ts/src/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.')
:marked
@ -77,7 +78,7 @@ table(width="100%")
:marked
Respond when Angular (re)sets data-bound input properties.
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.
tr(style=top)
@ -86,15 +87,15 @@ table(width="100%")
:marked
Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties.
Called _once_, after the _first_ `ngOnChanges()`.
Called _once_, after the _first_ `ngOnChanges()`.
tr(style=top)
td <code>ngDoCheck()</code>
td
: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()`.
tr(style=top)
@ -142,13 +143,13 @@ table(width="100%")
td
:marked
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.
+ifDocsFor('ts|js')
a#interface-optional
.l-main-section
.l-main-section
:marked
## Interfaces are optional (technically)
@ -164,7 +165,7 @@ table(width="100%")
Nonetheless, it's good practice to add interfaces to TypeScript directive classes
in order to benefit from strong typing and editor tooling.
a#other-lifecycle-hooks
.l-main-section
:marked
@ -344,7 +345,7 @@ a#oninit
.l-sub-section
:marked
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.
:marked
@ -352,9 +353,9 @@ a#oninit
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.
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
[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.
@ -409,7 +410,7 @@ figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
: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`
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")
:marked
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency&mdash;after _every_
This hook is called with enormous frequency&mdash;after _every_
change detection cycle no matter where the change occurred.
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!).
block tick-methods
: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.
:marked
@ -497,7 +498,7 @@ a#aftercontent
## AfterContent
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls
*after* Angular projects external content into the component.
a#content-projection
:marked
### Content projection
@ -529,13 +530,13 @@ figure.image-display
:marked
.l-sub-section
:marked
The telltale signs of *content projection* are twofold:
The telltale signs of *content projection* are twofold:
- HTML between component element tags.
- The presence of `<ng-content>` tags in the component's template.
a#aftercontent-hooks
:marked
### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks.
*AfterContent* hooks are similar to the *AfterView* hooks.
The key difference is in the child component.
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags

View File

@ -3,7 +3,7 @@ block includes
// TODO
Images
:marked
**NgModules** help organize an application into cohesive blocks of functionality.
<!-- CF: "app" and "application" are used interchangeably throughout this page.
@ -23,12 +23,14 @@ block includes
This page covers NgModules in greater depth.
## 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 headings are H2, some are H3
- some pages don't have tables of contents
I didn't make changes here as I'm not sure what the correct style is.
-->
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")
* [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
@ -36,7 +38,7 @@ block includes
* [Providers](#providers "Extend the app with additional services")
* [Imports](#imports "Import components, directives, and pipes for use in component templates")
* [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")
* [Lazy loaded modules](#lazy-load "Load modules asynchronously") with the router
* [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
template-driven or
[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 `ContactComponent` is written in _template-driven_ style.
@ -366,10 +368,10 @@ a#imports
.alert.is-critical
:marked
*Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to
*Do not* add `NgModel`&mdash;or the `FORMS_DIRECTIVES`&mdash;to
the `AppModule` metadata's declarations.
These directives belong to the `FormsModule`.
Components, directives, and pipes belong to _one module only_.
*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=".")
:marked
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).
:marked
@ -464,7 +466,7 @@ a#resolve-conflicts
:marked
## Resolve directive conflicts
<!-- 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. -->
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,
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 -->
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`.
:marked
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.
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 declarations and contact providers.
* Delete the `FormsModule` from the `imports` list (`AppComponent` doesn't need it).
Leave only the classes required at the application root level.
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.
.l-sub-section
: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>
:marked
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
a difference with prior or future versions.
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
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 highlight.directive.ts
: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
[Routing & Navigation](router.html#child-routing-component) page.
The `HeroComponent` is the feature's top component and routing host.
@ -865,7 +867,7 @@ a#shared-module
`UserService` is an application-wide singleton.
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
<!-- 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. -->
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.
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:
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`.
Each must be registered exactly once, in the app root injector, when the application starts.
While many components inject such services in their constructors&mdash;and
therefore require JavaScript `import` statements to import their symbols&mdash;no
While many components inject such services in their constructors&mdash;and
therefore require JavaScript `import` statements to import their symbols&mdash;no
other component or module should define or re-create the services themselves.
Their _providers_ aren't shared.
@ -1026,7 +1028,7 @@ a#prevent-reimport
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.
<!-- 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. -->
You could hope that no developer makes that mistake.

View File

@ -18,6 +18,7 @@ block includes
The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs.
# Contents
* [Demos](#demos)
* [Providing HTTP Services](#http-providers)
* [The Tour of Heroes *HTTP* client demo](#http-client)
@ -224,13 +225,13 @@ a#HeroService
:marked
## RxJS library
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS Reactive Extensions">RxJS</a>
is a third party library, endorsed by Angular, that implements the
is a third party library, endorsed by Angular, that implements the
<a href="https://www.youtube.com/watch?v=VLGCCpOWFFw" target="_blank" title="Video: Rob Wormald on Observables"><b>asynchronous Observable</b></a> pattern.
All of the Developer Guide samples have installed the RxJS npm package
because Observables are used widely in Angular applications.
_This_ app needs it when working with the HTTP client.
But you must take a critical extra step to make RxJS Observables usable:
But you must take a critical extra step to make RxJS Observables usable:
_you must import the RxJS operators individually_.
### 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
back to the component for presentation to the user,
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.
+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
:marked
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`.
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
this example.
Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the
first *success* parameter and its `catch` callback to the second *fail* parameter
Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the
first *success* parameter and its `catch` callback to the second *fail* parameter
in this pattern: `.toPromise(this.extractData, this.handleError)`.
The `errorHandler` forwards an error message as a failed `Promise` instead of a failed `Observable`.
@ -442,15 +443,15 @@ block hero-list-comp-add-hero
:marked
The less obvious but critical difference is that these two methods return very different results.
The Promise-based `then()` returns another Promise. You can keep chaining
The Promise-based `then()` returns another Promise. You can keep chaining
more `then()` and `catch()` calls, getting a new promise each time.
The `subscribe()` method returns a `Subscription`. A `Subscription` is not another `Observable`.
It's the end of the line for Observables. You can't call `map()` on it or call `subscribe()` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
To understand the implications and consequences of subscriptions,
watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
To understand the implications and consequences of subscriptions,
watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
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')
: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
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.
+makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts','call-jsonp','src/app/wiki/wikipedia.service.ts (call jsonp)')(format=".")
: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.
<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,
and calls a `search(term)` method after each `keyup` event.
The component's `search(term)` method delegates to the `WikipediaService`, which returns an
The component's `search(term)` method delegates to the `WikipediaService`, which returns an
Observable !{_array} of string results (`Observable<string[]>`).
Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`,
the app forwards the Observable result to the template (via `items`) where the `async` pipe
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 [Pipes](pipes.html) page.
.l-sub-section
: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.
`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.
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
no matter which response arrives first.
@ -591,14 +592,14 @@ block wikipedia-jsonp+
+makeTabs(
`server-communication/ts/src/app/wiki/wiki-smart.component.ts,
server-communication/ts/src/app/wiki/wiki.component.ts`,
null,
server-communication/ts/src/app/wiki/wiki.component.ts`,
null,
`src/app/wiki/wiki-smart.component.ts,
src/app/wiki/wiki.component.ts`
)
:marked
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,
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.
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
a#listen-for-search-terms
:marked
### 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.
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
:marked
@ -650,15 +651,15 @@ a#xsrf
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.
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.
The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly
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.
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 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.
a#override-default-request-options
@ -667,7 +668,7 @@ a#override-default-request-options
## Override default request headers (and other request options)
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.
The `HttpModule` provides these default options via the `RequestOptions` token.
@ -692,10 +693,10 @@ a#override-default-request-options
:marked
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),
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
to verify the header is there.
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.
@ -720,13 +721,13 @@ a#in-mem-web-api
:marked
The *get heroes* scenario would work,
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.
.l-sub-section
:marked
The in-memory web api is not part of Angular _proper_.
It's an optional service in its own
The in-memory web api is not part of Angular _proper_.
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>
library installed with npm (see `package.json`).
@ -770,7 +771,7 @@ block redirect-to-web-api
+makeExcerpt('src/app/app.module.ts')
.alert.is-important
: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.
:marked
See the full source code in the <live-example></live-example>.

View File

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