docs(dart): decouple from TS .jade files temporarily (#2044)

Addresses #2043.
This commit is contained in:
Patrice Chalin 2016-08-08 15:48:45 -07:00 committed by Kathy Walrath
parent b796d4b97d
commit 5ce8304227
30 changed files with 7783 additions and 13 deletions

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/architecture.jade
extends ../../../ts/_cache/guide/architecture.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/attribute-directives.jade
extends ../../../ts/_cache/guide/attribute-directives.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/component-styles.jade
extends ../../../ts/_cache/guide/component-styles.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/dependency-injection.jade
extends ../../../ts/_cache/guide/dependency-injection.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/displaying-data.jade
extends ../../../ts/_cache/guide/displaying-data.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/hierarchical-dependency-injection.jade
extends ../../../ts/_cache/guide/hierarchical-dependency-injection.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/lifecycle-hooks.jade
extends ../../../ts/_cache/guide/lifecycle-hooks.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/pipes.jade
extends ../../../ts/_cache/guide/pipes.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/server-communication.jade
extends ../../../ts/_cache/guide/server-communication.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/structural-directives.jade
extends ../../../ts/_cache/guide/structural-directives.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/guide/template-syntax.jade
extends ../../../ts/_cache/guide/template-syntax.jade
block includes
include ../_util-fns

View File

@ -1,4 +1,4 @@
extends ../../ts/latest/quickstart.jade
extends ../../ts/_cache/quickstart.jade
block includes
include _util-fns

View File

@ -1,4 +1,4 @@
extends ../../../ts/latest/tutorial/toh-pt6.jade
extends ../../../ts/_cache/tutorial/toh-pt6.jade
block includes
include ../_util-fns

View File

@ -0,0 +1 @@
include ../latest/_quickstart_repo

View File

@ -0,0 +1 @@
include ../latest/_util-fns

View File

@ -0,0 +1,597 @@
block includes
include ../_util-fns
- var _library_module = 'library module'
- var _at_angular = '@angular'
:marked
Angular 2 is a framework to help us build client applications in HTML and
either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript.
block angular-parts
:marked
The framework consists of several cooperating libraries, some of them core and some optional.
:marked
With Angular, we write applications by composing HTML *templates* with Angularized-markup,
writing *component* classes to manage those templates, adding application logic in *services*,
and handing the top root component to Angular's *bootstrapper*.
Angular takes over, presenting our application content in a browser and
responding to user interactions according to the instructions we provided.
Of course there is more to it than this.
We'll learn the details when we dive into the guide chapters.
Let's get the big picture first.
figure
img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700")
:marked
The architecture diagram identifies the eight main building blocks of an Angular 2 application:
1. [Modules](#modules)
1. [Components](#components)
1. [Templates](#templates)
1. [Metadata](#metadata)
1. [Data binding](#data-binding)
1. [Directives](#directives)
1. [Services](#services)
1. [Dependency injection](#dependency-injection)
Learn these, and we're on our way.
.l-sub-section
p The code referenced in this chapter is available as a #[+liveExampleLink2()].
.l-main-section
:marked
## Modules
figure
img(src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
:marked
Angular apps are modular.
In general we assemble our application from many **modules**.
A typical module is a cohesive block of code dedicated to a single purpose.
A module **exports** something of value in that code, typically one thing such as a class.
<br clear="all"><br>
block modules-in-dart
//- N/A
block modules-are-optional
.l-sub-section
:marked
### Modules are optional
We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular
approach using that syntax. That's why we list *Module* among the basic building blocks.
Angular itself doesn't require a modular approach nor this particular syntax. Don't use it if you don't want it.
Each chapter has plenty to offer after you steer clear of the `import` and `export` statements.
Find setup and organization clues in the JavaScript track (select it from the combo-box at the top of this page)
which demonstrates Angular 2 development with plain old JavaScript and no module system.
- var _app_comp_filename = _docsFor == 'dart' ? 'app_component.dart' : 'app.component.ts';
:marked
Perhaps the first module we meet is a module that exports a *component* class.
The component is one of the basic Angular blocks, we write a lot of them,
and we'll talk about components in the next segment. For the moment it is enough to know that a
component class is the kind of thing we'd export from a module.
Most applications have an `AppComponent`. By convention, we'll find it in a file named `!{_app_comp_filename}`.
Look inside such a file and we'll see a declaration such as this one.
+makeExcerpt('app/app.component.ts ()', 'export')
block export-qualifier
:marked
The `export` statement tells TypeScript that this is a module whose
`AppComponent` class is public and accessible to other modules of the application.
:marked
When we need a reference to the `AppComponent`, we **import** it like this:
+makeExcerpt('app/main.ts', 'import')
block ts-import
:marked
The `import` statement tells the system it can get an `AppComponent` from a module named `app.component`
located in a neighboring file.
The **module name** (AKA module id) is often the same as the filename without its extension.
:marked
### Libraries
figure
img(src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
block angular-library-modules
:marked
Some modules are _libraries_ of other modules.
Angular itself ships as a collection of library modules within several npm packages.
Their names begin with the `!{_at_angular}` prefix.
Each Angular library contains a [barrel](../glossary.html#barrel) module
that is actually a public façade over several logically-related private modules.
:marked
`!{_at_angular}/core` is the primary Angular library from which we get most of what we need.
<br clear="all">
There are other important Angular libraries too, such as `!{_at_angular}/common`<span if-docs="ts">,
`!{_at_angular}/http`</span> and `!{_at_angular}/router`.
We import what we need from an Angular !{_library_module}.
block angular-imports
:marked
For example, we import the Angular **`Component` *function*** from `@angular/core` like this:
+makeExcerpt('app/app.component.ts', 'import')
:marked
Compare that syntax to our previous import of `AppComponent`.
+makeExcerpt('app/main.ts', 'import')
:marked
Notice the difference?
In the first case, when importing from an Angular library module,
the import statement refers to the bare module name, `@angular/core`, *without a path prefix*.
When we import from one of *our* own files, we prefix the module name with the file path.
In this example we specify a relative file path (`./`). That means the
source module is in the same folder (`./`) as the module importing it.
We could path up and around the application folder structure if the source module were somewhere else.
.l-sub-section
:marked
We import and export in the ECMAScript 2015 (ES2015) module syntax.
Learn more about that syntax [here](http://www.2ality.com/2014/09/es6-modules-final.html)
and many other places on the web.
The infrastructure *behind* module loading and importing is an important subject.
But it's a subject outside the scope of this introduction to Angular.
While we're focused on our application, *import* and *export*
is about all we need to know.
- var _export = _docsFor == 'dart' ? 'publicly declare' : 'export';
- var _declare = _docsFor == 'dart' ? 'declare' : 'export';
:marked
The key take-aways are:
* Angular apps are composed of modules.
* Modules !{_export} things &mdash; classes, function, values &mdash; that other modules import.
* We prefer to write our application as a collection of modules, each module exporting one thing.
The first module we write will most likely !{_declare} a component.
.l-main-section
:marked
## Components
figure
img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
:marked
A **component** controls a patch of screen real estate that we could call a *view*.
The shell at the application root with navigation links, a list of heroes, a hero editor ...
they're all views controlled by components.
We define a component's application logic &mdash; what it does to support the view &mdash; inside a class.
The class interacts with the view through an API of properties and methods.
<a id="component-code"></a>
A `HeroListComponent`, for example, might have a `heroes` property that returns !{_an} !{_array} of heroes
that it acquired from a service.
It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component might be a class like this:
+makeExcerpt('app/hero-list.component.ts', 'class')
:marked
Angular creates, updates, and destroys components as the user moves through the application.
The developer can take action at each moment in this lifecycle through optional [lifecycle hooks](lifecycle-hooks.html), like `ngOnInit()` declared above.
.l-sub-section
:marked
We may wonder who is calling the component's constructor? Who provides the service parameter?
For the moment, have faith that Angular will call the constructor and deliver an
appropriate `HeroService` when we need it.
.l-main-section
:marked
## Templates
figure
img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
:marked
We define a component's view with its companion **template**. A template is a form of HTML
that tells Angular how to render the component.
A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a
template for our `HeroListComponent`:
+makeExample('app/hero-list.component.html')
:marked
This template features typical HTML elements like `<h2>` and `<p>`.
But what are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>`?
These are examples of Angular's [template syntax](template-syntax.html).
We will grow accustomed to that syntax and may even learn to love it.
We'll begin to explain it in a moment.
Before we do, focus attention on the last line.
The `<hero-detail>` tag is a custom element representing the `HeroDetailComponent`.
The `HeroDetailComponent` is a *different* component than the `HeroListComponent` we've been reviewing.
The `HeroDetailComponent` (code not shown) presents facts about a particular hero, the
hero that the user selects from the list presented by the `HeroListComponent`.
The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
figure
img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" )
:marked
Notice how `<hero-detail>` rests comfortably among native HTML elements.
We can and _will_ mix our custom components with native HTML in the same layouts.
In this manner we'll compose complex component trees to build out our richly featured application.
<br clear="all">
.l-main-section
:marked
## Metadata
figure
img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" )
:marked
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
<br clear="all">
:marked
[Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class.
There is no evidence of a framework, no "Angular" in it at all.
In fact, it really is *just a class*. It's not a component until we *tell Angular about it*.
We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class.
In !{_Lang}, we attach metadata by using !{_a} **!{_decorator}**.
Here's some metadata for `HeroListComponent`:
+makeExcerpt('app/hero-list.component.ts', 'metadata')
:marked
Here we see the `@Component` !{_decorator} which (no surprise) identifies the class
immediately below it as a component class.
block ts-decorator
:marked
A decorator is a function. Decorators often have a configuration parameter.
The `@Component` decorator takes a required configuration object with the
information Angular needs to create and present the component and its view.
Here are a few of the possible `@Component` configuration options:
:marked
- `selector`: CSS selector that tells Angular to create and insert an instance of this component
where it finds a `<hero-list>` tag in *parent* HTML.
For example, if an app's HTML contains `<hero-list></hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags.
- `templateUrl`: address of this component's template, which we showed [above](#templates).
- `directives`: !{_array} of the components or directives that *this* template requires.
We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent`
in the space indicated by `<hero-detail>` tags.
Angular will do so only if we mention the `HeroDetailComponent` in this `directives` !{_array}.
- `providers`: !{_array} of **dependency injection providers** for services that the component requires.
This is one way to tell Angular that our component's constructor requires a `HeroService`
so it can get the list of heroes to display. We'll get to dependency injection later.
figure
img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" )
:marked
Angular reads the metadata specified by the `@Component`
annotation. That's how Angular learns to do "the right thing".
The template, metadata, and component together describe a view.
We apply other metadata !{_decorator}s in a similar fashion to guide Angular behavior.
`@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s
we'll master as our Angular knowledge grows.
<br clear="all">
:marked
The architectural takeaway is that we must add metadata to our code
so that Angular knows what to do.
.l-main-section
:marked
## Data binding
Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
read as any experienced jQuery programmer can attest.
figure
img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" )
:marked
Angular supports **data binding**,
a mechanism for coordinating parts of a template with parts of a component.
We add binding markup to the template HTML to tell Angular how to connect both sides.
There are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions &mdash;
as indicated by the arrows in the diagram.
<br clear="all">
:marked
We saw three forms of data binding in our [example](#templates) template:
+makeExcerpt('app/hero-list.component.1.html', 'binding')
:marked
* The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation)
displays the component's `hero.name` property value within the `<li>` tags.
* The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the value of `selectedHero` from
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `(click)` [*event binding*](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name.
**Two-way data binding** is an important fourth form
that combines property and event binding in a single notation, using the `ngModel` directive.
We didn't have a two-way binding in the `HeroListComponent` template;
here's an example from the `HeroDetailComponent` template:
+makeExcerpt('app/hero-detail.component.html', 'ngModel')
:marked
In two-way binding, a data property value flows to the input box from the component as with property binding.
The user's changes also flow back to the component, resetting the property to the latest value,
as with event binding.
Angular processes *all* data bindings once per JavaScript event cycle,
from the root of the application component tree down to the leaves.
figure
img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
:marked
We don't know all the details yet,
but it's clear from these examples that data binding plays an important role in communication
between a template and its component.
<br clear="all">
figure
img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
:marked
Data binding is also important for communication between parent and child components.
<br clear="all">
.l-main-section
:marked
## Directives
figure
img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" )
:marked
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
according to the instructions given by **directives**.
A directive is a class with directive metadata. In !{_Lang} we apply the `@Directive` !{_decorator}
to attach metadata to the class.
<br clear="all">
:marked
We already met one form of directive: the component. A component is a *directive-with-a-template*;
a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features.
.l-sub-section
:marked
While **a component is technically a directive**,
components are so distinctive and central to Angular applications that we chose
to separate components from directives in this architectural overview.
:marked
Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
They tend to appear within an element tag as attributes do,
sometimes by name but more often as the target of an assignment or a binding.
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
Our [example](#templates) template uses two built-in structural directives:
+makeExcerpt('app/hero-list.component.1.html', 'structural')
:marked
* [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists.
block dart-bool
//- N/A
:marked
**Attribute** directives alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name.
The `ngModel` directive, which implements two-way data binding, is
an example of an attribute directive. `ngModel` modifies the behavior of
an existing element (typically an `<input>`)
by setting its display value property and responding to change events.
+makeExcerpt('app/hero-detail.component.html', 'ngModel')
:marked
Angular ships with a small number of other directives that either alter the layout structure
(for example, [ngSwitch](template-syntax.html#ngSwitch))
or modify aspects of DOM elements and components
(for example, [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)).
Of course, we can also write our own directives. Components such as
`HeroListComponent` are one kind of custom directive.
<!-- PENDING: link to where to learn more about other kinds! -->
.l-main-section
:marked
## Services
figure
img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" )
:marked
_Service_ is a broad category encompassing any value, function, or feature that our application needs.
Almost anything can be a service.
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
<br clear="all">
:marked
Examples include:
* logging service
* data service
* message bus
* tax calculator
* application configuration
There is nothing specifically _Angular_ about services. Angular itself has no definition of a service.
There is no service base class, and no place to register a service.
Yet services are fundamental to any Angular application. Our components are big consumers of services.
Here's an example of a service class that logs to the browser console
+makeExcerpt('app/logger.service.ts', 'class')
:marked
Here's a `HeroService` that fetches heroes and returns them in a resolved !{_PromiseLinked}.
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
+makeExcerpt('app/hero.service.ts', 'class')
:marked
Services are everywhere.
We prefer our component classes lean. Our components don't fetch data from the server,
they don't validate user input, and they don't log directly to the console.
They delegate such tasks to services.
A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template)
and the application logic (which often includes some notion of a _model_).
A good component presents properties and methods for data binding.
It delegates everything nontrivial to services.
Angular doesn't *enforce* these principles.
It won't complain if we write a "kitchen sink" component with 3000 lines.
Angular does help us *follow* these principles by making it easy to factor our
application logic into services and make those services available to components through *dependency injection*.
.l-main-section
:marked
## Dependency injection
figure
img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" )
:marked
_Dependency injection_ is a way to supply a new instance of a class
with the fully-formed dependencies it requires. Most dependencies are services.
Angular uses dependency injection to provide new components with the services they need.
<br clear="all">
:marked
Angular can tell which services a component needs by looking at the types of its constructor parameters.
For example, the constructor of our `HeroListComponent` needs a `HeroService`:
+makeExcerpt('app/hero-list.component.ts (constructor)', 'ctor')
:marked
When Angular creates a component, it first asks an **injector** for
the services that the component requires.
An injector maintains a container of service instances that it has previously created.
If a requested service instance is not in the container, the injector makes one and adds it to the container
before returning the service to Angular.
When all requested services have been resolved and returned,
Angular can call the component's constructor with those services as arguments.
This is what we mean by *dependency injection*.
The process of `HeroService` injection looks a bit like this:
figure
img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" )
:marked
If the injector doesn't have a `HeroService`, how does it know how to make one?
In brief, we must have previously registered a **provider** of the `HeroService` with the injector.
A provider is something that can create or return a service, typically the service class itself.
We can register providers at any level of the application component tree.
We often do so at the root when we bootstrap the application so that
the same instance of a service is available everywhere.
+makeExcerpt('app/main.ts', 'bootstrap')
:marked
Alternatively, we might register at a component level, in the providers property of the `@Component` metadata:
+makeExcerpt('app/hero-list.component.ts', 'providers')
:marked
Registering at a component level means we get a new instance of the
service with each new instance of that component.
<!-- We've vastly oversimplified dependency injection for this overview.
The full story is in the [Dependency Injection](dependency-injection.html) chapter. -->
Points to remember about dependency injection:
* Dependency injection is wired into the Angular framework and used everywhere.
* The *injector* is the main mechanism.
* An injector maintains a *container* of service instances that it created.
* An injector can create a new service instance from a *provider*.
* A *provider* is a recipe for creating a service.
* We register *providers* with injectors.
.l-main-section
:marked
## Wrap up
We've learned just a bit about the eight main building blocks of an Angular application:
1. [Modules](#modules)
1. [Components](#components)
1. [Templates](#templates)
1. [Metadata](#metadata)
1. [Data binding](#data-binding)
1. [Directives](#directives)
1. [Services](#services)
1. [Dependency injection](#dependency-injection)
That's a foundation for everything else in an Angular application,
and it's more than enough to get going.
But it doesn't include everything we'll need or want to know.
Here is a brief, alphabetical list of other important Angular features and services.
Most of them are covered in this Developers Guide (or soon will be).
> [**Animations**](animations.html): The animation library makes it easy for developers to animate component behavior
without deep knowledge of animation techniques or CSS.
> **Bootstrap**: A method to configure and launch the root application component.
> **Change detection**: Learn how Angular decides that a component property value has changed and
when to update the screen.
Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies.
> **Component router**: With the component Router service, users can navigate a multi-screen application
in a familiar web browsing style using URLs.
> **Events**: The DOM raises events. So can components and services. Angular offers mechanisms for
publishing and subscribing to events.
> [**Forms**](forms.html): Support complex data entry scenarios with HTML-based validation and dirty checking.
> [**HTTP**](server-communication.html): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
> [**Lifecycle hooks**](lifecycle-hooks.html): We can tap into key moments in the lifetime of a component, from its creation to its destruction,
by implementing the lifecycle hook interfaces.
> [**Pipes**](pipes.html): Services that transform values for display.
We can put pipes in our templates to improve the user experience. Consider
this `currency` pipe expression:
<div style="margin-left:40px">
code-example().
price | currency:'USD':true
</div>
:marked
> It displays a price of "42.33" as `$42.33`.
> [**Router**](router.html): Navigate from page to page within the client
application and never leave the browser.
> [**Testing**](testing.html): Angular provides a
[testing library](https://pub.dartlang.org/packages/angular2_testing)
to run unit tests on our application parts as they interact with the Angular framework.

View File

@ -0,0 +1,372 @@
block includes
include ../_util-fns
:marked
An **Attribute** directive changes the appearance or behavior of a DOM element.
:marked
In this chapter we will
* [write an attribute directive to change the background color](#write-directive)
* [apply the attribute directive to an element in a template](#apply-directive)
* [respond to user-initiated events](#respond-to-user)
* [pass values into the directive using data binding](#bindings)
Try the <live-example></live-example>.
## Directives overview
There are three kinds of directives in Angular:
1. Components
1. Structural directives
1. Attribute directives
A *Component* is really a directive with a template.
It's the most common of the three directives and we tend to write lots of them as we build applications.
[*Structural* directives](structural-directives.html) can change the DOM layout by adding and removing DOM elements.
[NgFor](template-syntax.html#ngFor) and [NgIf](template-syntax.html#ngIf) are two familiar examples.
An *Attribute* directive can change the appearance or behavior of an element.
The built-in [NgStyle](template-syntax.html#ngStyle) directive, for example,
can change several element styles at the same time.
We are going to write our own attribute directive to set an element's background color
when the user hovers over that element.
.l-sub-section
:marked
We don't need *any* directive to simply set the background color.
We can set it with the special [Style Binding](template-syntax.html#style-binding) like this:
+makeExample('attribute-directives/ts/app/app.component.1.html','p-style-background')
:marked
That wouldn't be nearly as much fun as creating our own directive.
Besides, we're not just *setting* the color; we'll be *changing* the color
in response to a user action, a mouse hover.
.l-main-section
a#write-directive
:marked
## Build a simple attribute directive
An attribute directive minimally requires building a controller class annotated with
`@Directive`, which specifies the selector identifying
the attribute associated with the directive.
The controller class implements the desired directive behavior.
Let's build a small illustrative example together.
:marked
### Our first draft
Create a new project folder (`attribute-directives`) and follow the steps in the [QuickStart](../quickstart.html).
include ../_quickstart_repo
:marked
Create the following source file in the indicated folder with the given code:
+makeExample('app/highlight.directive.1.ts')
block highlight-directive-1
:marked
We begin by importing some symbols from the Angular `core`.
We need the `Directive` symbol for the `@Directive` decorator.
We need the `ElementRef` to [inject](dependency-injection.html) into the directive's constructor
so we can access the DOM element.
We don't need `Input` immediately but we will need it later in the chapter.
Then we define the directive metadata in a configuration object passed
as an argument to the `@Directive` decorator function.
:marked
`@Directive` requires a CSS selector to identify
the HTML in the template that is associated with our directive.
The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
is the attribute name in square brackets.
Our directive's selector is `[myHighlight]`.
Angular will locate all elements in the template that have an attribute named `myHighlight`.
.l-sub-section
:marked
### Why not call it "highlight"?
*highlight* is a nicer name than *myHighlight* and, technically, it would work if we called it that.
However, we recommend picking a selector name with a prefix to ensure
that it cannot conflict with any standard HTML attribute, now or in the future.
There is also less risk of colliding with a third-party directive name when we give ours a prefix.
We do **not** prefix our `highlight` directive name with **`ng`**.
That prefix belongs to Angular.
We need a prefix of our own, preferably short, and `my` will do for now.
p
| After the #[code @Directive] metadata comes the directive's controller class, which contains the logic for the directive.
+ifDocsFor('ts')
| We export `HighlightDirective` to make it accessible to other components.
:marked
Angular creates a new instance of the directive's controller class for
each matching element, injecting an Angular `ElementRef`
into the constructor.
`ElementRef` is a service that grants us direct access to the DOM element
through its `nativeElement` property.
That's all we need to set the element's background color using the browser DOM API.
.l-main-section
a#apply-directive
:marked
## Apply the attribute directive
The `AppComponent` in this sample is a test harness for our `HighlightDirective`.
Let's give it a new template that
applies the directive as an attribute to a paragraph (`p`) element.
In Angular terms, the `<p>` element will be the attribute **host**.
p
| We'll put the template in its own
code #[+adjExPath('app.component.html')]
| file that looks like this:
+makeExample('attribute-directives/ts/app/app.component.1.html',null,'app/app.component.html')(format=".")
:marked
A separate template file is clearly overkill for a 2-line template.
Hang in there; we're going to expand it later.
Meanwhile, we'll revise the `AppComponent` to reference this template.
+makeExample('attribute-directives/ts/app/app.component.ts',null,'app/app.component.ts')
:marked
We've added an `import` statement to fetch the 'Highlight' directive and,
added that class to a `directives` component metadata so that Angular
will recognize our directive when it encounters `myHighlight` in the template.
We run the app and see that our directive highlights the paragraph text.
figure.image-display
img(src="/resources/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight")
.l-sub-section
:marked
### Your directive isn't working?
Did you remember to set the `directives` attribute of `@Component`? It is easy to forget!
Open the console in the browser tools and look for an error like this:
code-example(format="nocode").
EXCEPTION: Template parse errors:
Can't bind to 'myHighlight' since it isn't a known native property
:marked
Angular detects that we're trying to bind to *something* but it doesn't know what.
We have to tell it by listing `HighlightDirective` in the `directives` metadata array.
:marked
Let's recap what happened.
Angular found the `myHighlight` attribute on the `<p>` element. It created
an instance of the `HighlightDirective` class,
injecting a reference to the element into the constructor
where we set the `<p>` element's background style to yellow.
.l-main-section
a#respond-to-user
:marked
## Respond to user action
We are not satisfied to simply set an element color.
Our directive should set the color in response to a user action.
Specifically, we want to set the color when the user hovers over an element.
We'll need to
1. detect when the user hovers into and out of the element,
2. respond to those actions by setting and clearing the highlight color, respectively.
We apply the `@HostListener` !{_decorator} to methods which are called when an event is raised.
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','host')(format=".")
.l-sub-section
:marked
The `@HostListener` !{_decorator} refers to the DOM element that hosts our attribute directive, the `<p>` in our case.
We could have attached event listeners by manipulating the host DOM element directly, but
there are at least three problems with such an approach:
1. We have to write the listeners correctly.
1. We must *detach* our listener when the directive is destroyed to avoid memory leaks.
1. We'd be talking to DOM API directly which, we learned, is something to avoid.
Let's roll with the `@HostListener` !{_decorator}.
:marked
Now we implement the two mouse event handlers:
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','mouse-methods')(format=".")
:marked
Notice that they delegate to a helper method that sets the color via a private local variable, `#{_priv}el`.
We revise the constructor to capture the `ElementRef.nativeElement` in this variable.
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','ctor')(format=".")
:marked
Here's the updated directive:
+makeExample('app/highlight.directive.2.ts')
:marked
We run the app and confirm that the background color appears as we move the mouse over the `p` and
disappears as we move out.
figure.image-display
img(src="/resources/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight")
.l-main-section
a#bindings
:marked
## Configure the directive with binding
Currently the highlight color is hard-coded within the directive. That's inflexible.
We should set the color externally with a binding like this:
+makeExample('attribute-directives/ts/app/app.component.html','pHost')
:marked
We'll extend our directive class with a bindable **input** `highlightColor` property and use it when we highlight text.
Here is the final version of the class:
+makeExcerpt('app/highlight.directive.ts', 'class')
a#input
:marked
The new `highlightColor` property is called an *input* property because data flows from the binding expression into our directive.
Notice the `@Input()` #{_decorator} applied to the property.
+makeExcerpt('app/highlight.directive.ts', 'color')
:marked
`@Input` adds metadata to the class that makes the `highlightColor` property available for
property binding under the `myHighlight` alias.
We must add this input metadata or Angular will reject the binding.
See the [appendix](#why-input) below to learn why.
.l-sub-section
:marked
### @Input(_alias_)
The developer who uses this directive expects to bind to the attribute name, `myHighlight`.
The directive property name is `highlightColor`. That's a disconnect.
We could resolve the discrepancy by renaming the property to `myHighlight` and define it as follows:
+makeExcerpt('app/highlight.directive.ts', 'highlight', '')
:marked
Maybe we don't want that property name inside the directive perhaps because it
doesn't express our intention well.
We can **alias** the `highlightColor` property with the attribute name by
passing `myHighlight` into the `@Input` #{_decorator}:
+makeExcerpt('app/highlight.directive.ts', 'color', '')
:marked
Now that we're getting the highlight color as an input, we modify the `onMouseEnter()` method to use
it instead of the hard-coded color name.
We also define red as the default color to fallback on in case
the user neglects to bind with a color.
+makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter', '')
:marked
Now we'll update our `AppComponent` template to let
users pick the highlight color and bind their choice to our directive.
Here is the updated template:
+makeExcerpt('attribute-directives/ts/app/app.component.html', 'v2', '')
.l-sub-section
:marked
### Where is the templated *color* property?
The eagle-eyed may notice that the radio button click handlers in the template set a `color` property
and we are binding that `color` to the directive.
We should expect to find a `color` on the host `AppComponent`.
**We never defined a color property for the host *AppComponent***!
And yet this code works. Where is the template `color` value going?
Browser debugging reveals that Angular dynamically added a `color` property
to the runtime instance of the `AppComponent`.
This is *convenient* behavior but it is also *implicit* behavior that could be confusing.
While it's cool that this technique works, we recommend adding the `color` property to the `AppComponent`.
:marked
Here is our second version of the directive in action.
figure.image-display
img(src="/resources/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2")
.l-main-section
:marked
## Bind to a second property
Our directive only has a single, customizable property. What if we had ***two properties***?
Let's allow the template developer to set the default color, the color that prevails until the user picks a highlight color.
We'll add a second **input** property to `HighlightDirective` called `defaultColor`:
+makeExample('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor')(format=".")
:marked
The `defaultColor` property has a setter that overrides the hard-coded default color, "red".
We don't need a getter.
How do we bind to it? We already "burned" the `myHighlight` attribute name as a binding target.
Remember that a *component is a directive too*.
We can add as many component property bindings as we need by stringing them along in the template
as in this example that sets the `a`, `b`, `c` properties to the string literals 'a', 'b', and 'c'.
code-example(format="." ).
&lt;my-component [a]="'a'" [b]="'b'" [c]="'c'">&lt;my-component>
:marked
We do the same thing with an attribute directive.
+makeExample('attribute-directives/ts/app/app.component.html', 'defaultColor')(format=".")
:marked
Here we're binding the user's color choice to the `myHighlight` attribute as we did before.
We're *also* binding the literal string, 'violet', to the `defaultColor`.
Here is the final version of the directive in action.
figure.image-display
img(src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight")
.l-main-section
:marked
## Summary
We now know how to
- [build a simple **attribute directive** to attach behavior to an HTML element](#write-directive),
- [use that directive in a template](#apply-directive),
- [respond to **events** to change behavior based on an event](#respond-to-user),
- and [use **binding** to pass values to the attribute directive](#bindings).
The final source:
+makeTabs(
`attribute-directives/ts/app/app.component.ts,
attribute-directives/ts/app/app.component.html,
attribute-directives/ts/app/highlight.directive.ts,
attribute-directives/ts/app/main.ts,
attribute-directives/ts/index.html
`,
',,full',
`app.component.ts,
app.component.html,
highlight.directive.ts,
main.ts,
index.html
`)
a#why-input
.l-main-section
:marked
### Appendix: Input properties
Earlier we declared the `highlightColor` property to be an ***input*** property of our
`HighlightDirective`
We've seen properties in bindings before.
We never had to declare them as anything. Why now?
Angular makes a subtle but important distinction between binding **sources** and **targets**.
In all previous bindings, the directive or component property was a binding ***source***.
A property is a *source* if it appears in the template expression to the ***right*** of the equals (=).
A property is a *target* when it appears in **square brackets** ([ ]) to the **left** of the equals (=) ...
as it is does when we bind to the `myHighlight` property of the `HighlightDirective`,
+makeExample('attribute-directives/ts/app/app.component.html','pHost')(format=".")
:marked
The 'color' in `[myHighlight]="color"` is a binding ***source***.
A source property doesn't require a declaration.
The 'myHighlight' in `[myHighlight]="color"` *is* a binding ***target***.
We must declare it as an *input* property.
Angular rejects the binding with a clear error if we don't.
Angular treats a *target* property differently for a good reason.
A component or directive in target position needs protection.
Imagine that our `HighlightDirective` did truly wonderous things.
We graciously made a gift of it to the world.
To our surprise, some people &mdash; perhaps naively &mdash;
started binding to *every* property of our directive.
Not just the one or two properties we expected them to target. *Every* property.
That could really mess up our directive in ways we didn't anticipate and have no desire to support.
The *input* declaration ensures that consumers of our directive can only bind to
the properties of our public API ... nothing else.

View File

@ -0,0 +1,319 @@
block includes
include ../_util-fns
:marked
Angular 2 applications are styled with regular CSS. That means we can apply
everything we know about CSS stylesheets, selectors, rules, and media queries
to our Angular applications directly.
On top of this, Angular has the ability to bundle *component styles*
with our components enabling a more modular design than regular stylesheets.
In this chapter we learn how to load and apply these *component styles*.
# Table Of Contents
* [Using Component Styles](#using-component-styles)
* [Special selectors](#special-selectors)
* [Loading Styles into Components](#loading-styles)
* [Controlling View Encapsulation: Emulated, Native, and None](#view-encapsulation)
* [Appendix 1: Inspecting the generated runtime component styles](#inspect-generated-css)
* [Appendix 2: Loading Styles with Relative URLs](#relative-urls)
Run the <live-example></live-example> of the code shown in this chapter.
.l-main-section
:marked
## Using Component Styles
For every Angular 2 component we write, we may define not only an HTML template,
but also the CSS styles that go with that template,
specifying any selectors, rules, and media queries that we need.
One way to do this is to set the `styles` property in the component metadata.
The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
Usually we give it one string as in this example:
+makeExample('component-styles/ts/app/hero-app.component.ts')(format='.')
:marked
Component styles differ from traditional, global styles in a couple of ways.
Firstly, the selectors we put into a component's styles *only apply within the template
of that component*. The `h1` selector in the example above only applies to the `<h1>` tag
in the template of `HeroAppComponent`. Any `<h1>` elements elsewhere in
the application are unaffected.
This is a big improvement in modularity compared to how CSS traditionally works:
1. We can use the CSS class names and selectors that make the most sense in the context of each component.
1. Class names and selectors are local to the component and won't collide with
classes and selectors used elsewhere in the application.
1. Our component's styles *cannot* be changed by changes to styles elsewhere in the application.
1. We can co-locate the CSS code of each component with the TypeScript and HTML code of the component,
which leads to a neat and tidy project structure.
1. We can change or remove component CSS code in the future without trawling through the
whole application to see where else it may have been used. We just look at the component we're in.
a(id="special-selectors")
.l-main-section
:marked
## Special selectors
Component styles have a few special *selectors* from the world of
[shadow DOM style scoping](https://www.w3.org/TR/css-scoping-1):
### :host
Use the `:host` pseudo-class selector to target styles in the element that *hosts* the component (as opposed to
targeting elements *inside* the component's template):
+makeExample('component-styles/ts/app/hero-details.component.css', 'host')(format='.')
:marked
This is the *only* way we can target the host element. We cannot reach
it from inside the component with other selectors, because it is not part of the
component's own template. It is in a parent component's template.
Use the *function form* to apply host styles conditionally by
including another selector inside parentheses after `:host`.
In the next example we target the host element again, but only when it also has the `active` CSS class.
+makeExample('component-styles/ts/app/hero-details.component.css', 'hostfunction')(format=".")
:marked
### :host-context
Sometimes it is useful to apply styles based on some condition *outside* a component's view.
For example, there may be a CSS theme class applied to the document `<body>` element, and
we want to change how our component looks based on that.
Use the `:host-context()` pseudo-class selector. It works just like the function
form of `:host()`. It looks for a CSS class in *any ancestor* of the component host element, all the way
up to the document root. It's useful when combined with another selector.
In the following example, we apply a `background-color` style to all `<h2>` elements *inside* the component, only
if some ancestor element has the CSS class `theme-light`.
+makeExample('component-styles/ts/app/hero-details.component.css', 'hostcontext')(format='.')
:marked
### /deep/
Component styles normally apply only to the HTML in the component's own template.
We can use the `/deep/` selector to force a style down through the child component tree into all the child component views.
The `/deep/` selector works to any depth of nested components, and it applies *both to the view
children and the content children* of the component.
In this example, we target all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM:
+makeExample('component-styles/ts/app/hero-details.component.css', 'deep')(format=".")
:marked
The `/deep/` selector also has the alias `>>>`. We can use either of the two interchangeably.
.alert.is-important
:marked
The `/deep/` and `>>>` selectors should only be used with **emulated** view encapsulation.
This is the default and it is what we use most of the time. See the
[Controlling View Encapsulation](#view-encapsulation)
section for more details.
a(id='loading-styles')
.l-main-section
:marked
## Loading Styles into Components
We have several ways to add styles to a component:
* inline in the template HTML
* by setting `styles` or `styleUrls` metadata
* with CSS imports
The scoping rules outlined above apply to each of these loading patterns.
### Styles in Metadata
We can add a `styles` #{_array} property to the `@Component` #{_decorator}.
Each string in the #{_array} (usually just one string) defines the CSS.
+makeExample('component-styles/ts/app/hero-app.component.ts')
:marked
### Template Inline Styles
We can embed styles directly into the HTML template by putting them
inside `<style>` tags.
+makeExample('component-styles/ts/app/hero-controls.component.ts', 'inlinestyles')
:marked
### Style URLs in Metadata
We can load styles from external CSS files by adding a `styleUrls` attribute
into a component's `@Component` #{_decorator}:
+makeExample('component-styles/ts/app/hero-details.component.ts', 'styleurls')
block style-url
.alert.is-important
:marked
The URL is ***relative to the application root*** which is usually the
location of the `index.html` web page that hosts the application.
The style file URL is *not* relative to the component file.
That's why the example URL begins `app/`.
See [Appendix 2](#relative-urls) to specify a URL relative to the
component file.
block module-bundlers
.l-sub-section
:marked
Users of module bundlers like Webpack may also use the `styles` attribute
to load styles from external files at build time. They could write:
`styles: [require('my.component.css')]`
We set the `styles` property, **not** `styleUrls` property! The module
bundler is loading the CSS strings, not Angular.
Angular only sees the CSS strings *after* the bundler loads them.
To Angular it is as if we wrote the `styles` array by hand.
Refer to the module bundler's documentation for information on
loading CSS in this manner.
:marked
### Template Link Tags
We can also embed `<link>` tags into the component's HTML template.
As with `styleUrls`, the link tag's `href` URL is relative to the
application root, not relative to the component file.
+makeExample('component-styles/ts/app/hero-team.component.ts', 'stylelink')
:marked
### CSS @imports
We can also import CSS files into our CSS files by using the standard CSS
[`@import` rule](https://developer.mozilla.org/en/docs/Web/CSS/@import).
block css-import-url
:marked
In *this* case the URL is relative to the CSS file into which we are importing.
+makeExample('component-styles/ts/app/hero-details.component.css', 'import', 'app/hero-details.component.css (excerpt)')
a#view-encapsulation
.l-main-section
:marked
## Controlling View Encapsulation: Native, Emulated, and None
As discussed above, component CSS styles are *encapsulated* into the component's own view and do
not affect the rest of the application.
We can control how this encapsulation happens on a *per
component* basis by setting the *view encapsulation mode* in the component metadata. There
are three modes to choose from:
* `Native` view encapsulation uses the browser's native [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
implementation to attach a Shadow DOM to the component's host element, and then puts the component
view inside that Shadow DOM. The component's styles are included within the Shadow DOM.
* `Emulated` view encapsulation (**the default**) emulates the behavior of Shadow DOM by preprocessing
(and renaming) the CSS code to effectively scope the CSS to the component's view.
See [Appendix 1](#inspect-generated-css) for details.
* `None` means that Angular does no view encapsulation.
Angular adds the CSS to the global styles.
The scoping rules, isolations, and protections discussed earlier do not apply.
This is essentially the same as pasting the component's styles into the HTML.
Set the components encapsulation mode using the `encapsulation` property in the component metadata:
+makeExample('component-styles/ts/app/quest-summary.component.ts', 'encapsulation.native')(format='.')
:marked
`Native` view encapsulation only works on [browsers that have native support
for Shadow DOM](http://caniuse.com/#feat=shadowdom). The support is still limited,
which is why `Emulated` view encapsulation is the default mode and recommended
in most cases.
a#inspect-generated-css
.l-main-section
:marked
## Appendix 1: Inspecting The CSS Generated in Emulated View Encapsulation
When using the default emulated view encapsulation, Angular preprocesses
all component styles so that they approximate the standard Shadow CSS scoping rules.
When we inspect the DOM of a running Angular application with emulated view
encapsulation enabled, we see that each DOM element has some extra attributes
attached to it:
code-example(format="").
&lt;hero-details _nghost-pmm-5>
&lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2>
&lt;hero-team _ngcontent-pmm-5 _nghost-pmm-6>
&lt;h3 _ngcontent-pmm-6>Team&lt;/h3>
&lt;/hero-team>
&lt;/hero-detail>
:marked
We see two kinds of generated attributes:
* An element that would be a Shadow DOM host in native encapsulation has a
generated `_nghost` attribute. This is typically the case for component host elements.
* An element within a component's view has a `_ngcontent` attribute
that identifies to which host's emulated Shadow DOM this element belongs.
The exact values of these attributes are not important. They are automatically
generated and we never refer to them in application code. But they are targeted
by the generated component styles, which we'll find in the `<head>` section of the DOM:
code-example(format="").
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
:marked
These are the styles we wrote, post-processed so that each selector is augmented
with `_nghost` or `_ngcontent` attribute selectors.
These extra selectors enable the scoping rules described in this guide.
We'll likely live with *emulated* mode until shadow DOM gains traction.
a#relative-urls
.l-main-section
:marked
## Appendix 2: Loading Styles with Relative URLs
It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory:
code-example(format='').
quest-summary.component.ts
quest-summary.component.html
quest-summary.component.css
:marked
We include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
Because these files are co-located with the component,
it would be nice to refer to them by name without also having to specify a path back to the root of the application.
block module-id
:marked
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`.
+makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts')
:marked
Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter.

View File

@ -0,0 +1,897 @@
block includes
include ../_util-fns
- var _thisDot = 'this.';
:marked
**Dependency injection** is an important application design pattern.
Angular has its own dependency injection framework, and
we really can't build an Angular application without it.
It's used so widely that almost everyone just calls it _DI_.
In this chapter we'll learn what DI is and why we want it.
Then we'll learn [how to use it](#angular-di) in an Angular app.
- [Why dependency injection?](#why-dependency-injection)
- [Angular dependency injection](#angular-dependency-injection)
- [Injector providers](#injector-providers)
- [Dependency injection tokens](#dependency-injection-tokens)
- [Summary](#summary)
Run the <live-example></live-example>.
.l-main-section#why-di
:marked
## Why dependency injection?
Let's start with the following code.
+makeExample('dependency-injection/ts/app/car/car-no-di.ts', 'car', 'app/car/car.ts (without DI)')
:marked
Our `Car` creates everything it needs inside its constructor.
What's the problem?
The problem is that our `Car` class is brittle, inflexible, and hard to test.
Our `Car` needs an engine and tires. Instead of asking for them,
the `Car` constructor instantiates its own copies from
the very specific classes `Engine` and `Tires`.
What if the `Engine` class evolves and its constructor requires a parameter?
Our `Car` is broken and stays broken until we rewrite it along the lines of
`#{_thisDot}engine = new Engine(theNewParameter)`.
We didn't care about `Engine` constructor parameters when we first wrote `Car`.
We don't really care about them now.
But we'll *have* to start caring because
when the definition of `Engine` changes, our `Car` class must change.
That makes `Car` brittle.
What if we want to put a different brand of tires on our `Car`? Too bad.
We're locked into whatever brand the `Tires` class creates. That makes our `Car` inflexible.
Right now each new car gets its own engine. It can't share an engine with other cars.
While that makes sense for an automobile engine,
we can think of other dependencies that should be shared, such as the onboard
wireless connection to the manufacturer's service center. Our `Car` lacks the flexibility
to share services that have been created previously for other consumers.
When we write tests for our `Car` we're at the mercy of its hidden dependencies.
Is it even possible to create a new `Engine` in a test environment?
What does `Engine`itself depend upon? What does that dependency depend on?
Will a new instance of `Engine` make an asynchronous call to the server?
We certainly don't want that going on during our tests.
What if our `Car` should flash a warning signal when tire pressure is low?
How do we confirm that it actually does flash a warning
if we can't swap in low-pressure tires during the test?
We have no control over the car's hidden dependencies.
When we can't control the dependencies, a class becomes difficult to test.
How can we make `Car` more robust, flexible, and testable?
<a id="ctor-injection"></a>
That's super easy. We change our `Car` constructor to a version with DI:
+makeTabs(
'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts',
'car-ctor, car-ctor',
'app/car/car.ts (excerpt with DI), app/car/car.ts (excerpt without DI)')(format=".")
:marked
See what happened? We moved the definition of the dependencies to the constructor.
Our `Car` class no longer creates an engine or tires.
It just consumes them.
block ctor-syntax
.l-sub-section
:marked
We also leveraged TypeScript's constructor syntax for declaring
parameters and properties simultaneously.
:marked
Now we create a car by passing the engine and tires to the constructor.
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '')(format=".")
:marked
How cool is that?
The definition of the engine and tire dependencies are
decoupled from the `Car` class itself.
We can pass in any kind of engine or tires we like, as long as they
conform to the general API requirements of an engine or tires.
If someone extends the `Engine` class, that is not `Car`'s problem.
.l-sub-section
:marked
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
something like this:
- var stylePattern = { otl: /(new Car.*$)/gm };
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-param', '', stylePattern)(format=".")
:marked
The critical point is this: `Car` itself did not have to change.
We'll take care of the consumer's problem soon enough.
:marked
The `Car` class is much easier to test because we are in complete control
of its dependencies.
We can pass mocks to the constructor that do exactly what we want them to do
during each test:
- var stylePattern = { otl: /(new Car.*$)/gm };
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".")
:marked
**We just learned what dependency injection is**.
It's a coding pattern in which a class receives its dependencies from external
sources rather than creating them itself.
Cool! But what about that poor consumer?
Anyone who wants a `Car` must now
create all three parts: the `Car`, `Engine`, and `Tires`.
The `Car` class shed its problems at the consumer's expense.
We need something that takes care of assembling these parts for us.
We could write a giant class to do that:
+makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts')
:marked
It's not so bad now with only three creation methods.
But maintaining it will be hairy as the application grows.
This factory is going to become a huge spiderweb of
interdependent factory methods!
Wouldn't it be nice if we could simply list the things we want to build without
having to define which dependency gets injected into what?
This is where the dependency injection framework comes into play.
Imagine the framework had something called an _injector_.
We register some classes with this injector, and it figures out how to create them.
When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".")
:marked
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
The consumer knows nothing about creating a `Car`.
We don't have a gigantic factory class to maintain.
Both `Car` and consumer simply ask for what they need and the injector delivers.
This is what a **dependency injection framework** is all about.
Now that we know what dependency injection is and appreciate its benefits,
let's see how it is implemented in Angular.
.l-main-section#angular-di
:marked
## Angular dependency injection
Angular ships with its own dependency injection framework. This framework can also be used
as a standalone module by other applications and frameworks.
That sounds nice. What does it do for us when building components in Angular?
Let's see, one step at a time.
We'll begin with a simplified version of the `HeroesComponent`
that we built in the [The Tour of Heroes](../tutorial/).
+makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.1.ts,
dependency-injection/ts/app/heroes/hero-list.component.1.ts,
dependency-injection/ts/app/heroes/hero.ts,
dependency-injection/ts/app/heroes/mock-heroes.ts`,
'v1,,,',
`app/heroes/heroes.component.ts,
app/heroes/hero-list.component.ts,
app/heroes/hero.ts,
app/heroes/mock-heroes.ts`)
:marked
The `HeroesComponent` is the root component of the *Heroes* feature area.
It governs all the child components of this area.
Our stripped down version has only one child, `HeroListComponent`,
which displays a list of heroes.
:marked
Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection
defined in another file.
That may suffice in the early stages of development, but it's far from ideal.
As soon as we try to test this component or want to get our heroes data from a remote server,
we'll have to change the implementation of `heroes` and
fix every other use of the `HEROES` mock data.
Let's make a service that hides how we get hero data.
.l-sub-section
:marked
Given that the service is a
[separate concern](https://en.wikipedia.org/wiki/Separation_of_concerns),
we suggest that you
write the service code in its own file.
+ifDocsFor('ts')
:marked
See [this note](#one-class-per-file) for details.
+makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' )
:marked
Our `HeroService` exposes a `getHeroes` method that returns
the same mock data as before, but none of its consumers need to know that.
.l-sub-section
:marked
Notice the `@Injectable()` #{_decorator} above the service class.
We'll discuss its purpose [shortly](#injectable).
- var _perhaps = _docsFor == 'dart' ? '' : 'perhaps';
.l-sub-section
:marked
We aren't even pretending this is a real service.
If we were actually getting data from a remote server, the API would have to be
asynchronous, #{_perhaps} returning a !{_PromiseLinked}.
We'd also have to rewrite the way components consume our service.
This is important in general, but not to our current story.
:marked
A service is nothing more than a class in Angular 2.
It remains nothing more than a class until we register it with an Angular injector.
#bootstrap
:marked
### Configuring the injector
We don't have to create an Angular injector.
Angular creates an application-wide injector for us during the bootstrap process.
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.')
:marked
We do have to configure the injector by registering the **providers**
that create the services our application requires.
We'll explain what [providers](#providers) are later in this chapter.
Before we do, let's see an example of provider registration during bootstrapping:
+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap-discouraged')(format='.')
:marked
The injector now knows about our `HeroService`.
An instance of our `HeroService` will be available for injection across our entire application.
Of course we can't help wondering about that comment telling us not to do it this way.
It *will* work. It's just not a best practice.
The bootstrap provider option is intended for configuring and overriding Angular's own
preregistered services, such as its routing support.
The preferred approach is to register application providers in application components.
Because the `HeroService` is used within the *Heroes* feature area &mdash;
and nowhere else &mdash; the ideal place to register it is in the top-level `HeroesComponent`.
:marked
### Registering providers in a component
Here's a revised `HeroesComponent` that registers the `HeroService`.
- var stylePattern = { otl: /(providers:.*),/ };
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts', 'full','app/heroes/heroes.component.ts', stylePattern)(format='.')
:marked
Look closely at the `providers` part of the `@Component` metadata.
An instance of the `HeroService` is now available for injection in this `HeroesComponent`
and all of its child components.
The `HeroesComponent` itself doesn't happen to need the `HeroService`.
But its child `HeroListComponent` does, so we head there next.
:marked
### Preparing the HeroListComponent for injection
The `HeroListComponent` should get heroes from the injected `HeroService`.
Per the dependency injection pattern, the component must ask for the service in its
constructor, [as we explained earlier](#ctor-injection).
It's a small change:
+makeTabs(
`dependency-injection/ts/app/heroes/hero-list.component.2.ts,
dependency-injection/ts/app/heroes/hero-list.component.1.ts`,
null,
`app/heroes/hero-list.component (with DI),
app/heroes/hero-list.component (without DI)`)
.l-sub-section
:marked
#### Focus on the constructor
Adding a parameter to the constructor isn't all that's happening here.
+makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".")
:marked
Note that the constructor parameter has the type `HeroService`, and that
the `HeroListComponent` class has an `@Component` #{_decorator}
(scroll up to confirm that fact).
Also recall that the parent component (`HeroesComponent`)
has `providers` information for `HeroService`.
The constructor parameter type, the `@Component` #{_decorator},
and the parent's `providers` information combine to tell the
Angular injector to inject an instance of
`HeroService` whenever it creates a new `HeroListComponent`.
#di-metadata
:marked
### Implicit injector creation
When we introduced the idea of an injector above, we showed how to
use it to create a new `Car`. Here we also show how such an injector
would be explicitly created:
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".")
:marked
We won't find code like that in the Tour of Heroes or any of our other samples.
We *could* write code that [explicitly creates an injector](#explicit-injector) if we *had* to, but we rarely do.
Angular takes care of creating and calling injectors
when it creates components for us &mdash; whether through HTML markup, as in `<hero-list></hero-list>`,
or after navigating to a component with the [router](./router.html).
If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
:marked
### Singleton services
Dependencies are singletons within the scope of an injector.
In our example, a single `HeroService` instance is shared among the
`HeroesComponent` and its `HeroListComponent` children.
However, Angular DI is an hierarchical injection
system, which means that nested injectors can create their own service instances.
Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter.
:marked
### Testing the component
We emphasized earlier that designing a class for dependency injection makes the class easier to test.
Listing dependencies as constructor parameters may be all we need to test application parts effectively.
For example, we can create a new `HeroListComponent` with a mock service that we can manipulate
under test:
+makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.')
.l-sub-section
:marked
Learn more in [Testing](../testing/index.html).
:marked
### When the service needs a service
Our `HeroService` is very simple. It doesn't have any dependencies of its own.
What if it had a dependency? What if it reported its activities through a logging service?
We'd apply the same *constructor injection* pattern,
adding a constructor that takes a `Logger` parameter.
Here is the revision compared to the original.
+makeTabs(
`dependency-injection/ts/app/heroes/hero.service.2.ts,
dependency-injection/ts/app/heroes/hero.service.1.ts`,
null,
`app/heroes/hero.service (v2),
app/heroes/hero.service (v1)`)
:marked
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `#{_priv}logger`.
We call that property within our `getHeroes` method when anyone asks for heroes.
//- FIXME refer to Dart API when that page becomes available.
- var injMetaUrl = 'https://angular.io/docs/ts/latest/api/core/index/InjectableMetadata-class.html';
h3#injectable Why @Injectable()?
:marked
**<a href="#{injMetaUrl}">@Injectable()</a>** marks a class as available to an
injector for instantiation. Generally speaking, an injector will report an
error when trying to instantiate a class that is not marked as
`@Injectable()`.
block injectable-not-always-needed-in-ts
.l-sub-section
:marked
As it happens, we could have omitted `@Injectable()` from our first
version of `HeroService` because it had no injected parameters.
But we must have it now that our service has an injected dependency.
We need it because Angular requires constructor parameter metadata
in order to inject a `Logger`.
.callout.is-helpful
header Suggestion: add @Injectable() to every service class
:marked
We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
and, therefore, do not technically require it. Here's why:
ul(style="font-size:inherit")
li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later.
li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{_a} #{_decorator} is missing.
:marked
Injectors are also responsible for instantiating components
like `HeroesComponent`. Why haven't we marked `HeroesComponent` as
`@Injectable()`?
We *can* add it if we really want to. It isn't necessary because the
`HeroesComponent` is already marked with `@Component`, and this
!{_decorator} class (like `@Directive` and `@Pipe`, which we'll learn about later)
is a subtype of <a href="#{injMetaUrl}">InjectableMetadata</a>. It is in
fact `InjectableMetadata` #{_decorator}s that
identify a class as a target for instantiation by an injector.
+ifDocsFor('ts')
.l-sub-section
:marked
At runtime, injectors can read class metadata in the transpiled JavaScript code
and use the constructor parameter type information
to determine what things to inject.
Not every JavaScript class has metadata.
The TypeScript compiler discards metadata by default.
If the `emitDecoratorMetadata` compiler option is true
(as it should be in the `tsconfig.json`),
the compiler adds the metadata to the generated JavaScript
for _every class with at least one decorator_.
While any decorator will trigger this effect, mark the service class with the
<a href="#{injMetaUrl}">InjectableMetadata</a> #{_decorator}
to make the intent clear.
.callout.is-critical
header Always include the parentheses
block always-include-paren
:marked
Always write `@Injectable()`, not just `@Injectable`.
Our application will fail mysteriously if we forget the parentheses.
.l-main-section#logger-service
:marked
## Creating and registering a logger service
We're injecting a logger into our `HeroService` in two steps:
1. Create the logger service.
1. Register it with the application.
Our logger service is quite simple:
+makeExample('dependency-injection/ts/app/logger.service.ts', null, 'app/logger.service.ts')
block real-logger
//- N/A
:marked
We're likely to need the same logger service everywhere in our application,
so we put it in the project's `#{_appDir}` folder, and
we register it in the `providers` #{_array} of the metadata for our application root component, `AppComponent`.
+makeExcerpt('app/providers.component.ts','providers-logger','app/app.component.ts (excerpt)')
:marked
If we forget to register the logger, Angular throws an exception when it first looks for the logger:
code-example(format="nocode").
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
:marked
That's Angular telling us that the dependency injector couldn't find the *provider* for the logger.
It needed that provider to create a `Logger` to inject into a new
`HeroService`, which it needed to
create and inject into a new `HeroListComponent`.
The chain of creations started with the `Logger` provider. *Providers* are the subject of our next section.
.l-main-section#providers
:marked
## Injector providers
A provider *provides* the concrete, runtime version of a dependency value.
The injector relies on **providers** to create instances of the services
that the injector injects into components and other services.
We must register a service *provider* with the injector, or it won't know how to create the service.
Earlier we registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppComponent` like this:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
- var implements = _docsFor == 'dart' ? 'implements' : 'looks and behaves like a '
- var objectlike = _docsFor == 'dart' ? '' : 'an object that behaves like '
- var loggerlike = _docsFor == 'dart' ? '' : 'We could provide a logger-like object. '
:marked
There are many ways to *provide* something that #{implements} `Logger`.
The `Logger` class itself is an obvious and natural provider.
But it's not the only way.
We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`.
We could provide a substitute class. #{loggerlike}
We could give it a provider that calls a logger factory function.
Any of these approaches might be a good choice under the right circumstances.
What matters is that the injector has a provider to go to when it needs a `Logger`.
//- Dart limitation: the provide function isn't const so it cannot be used in an annotation.
- var _andProvideFn = _docsFor == 'dart' ? '' : 'and <i>provide</i> object literal';
#provide
:marked
### The *Provider* class !{_andProvideFn}
:marked
We wrote the `providers` #{_array} like this:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-1')
:marked
This is actually a short-hand expression for a provider registration
<span if-docs="ts">
using a _provider_ object literal with two properties:
</span>
<span if-docs="dart">
that creates a new instance of the
[Provider](../api/core/index/Provider-class.html) class:
</span>
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-3')
block provider-ctor-args
- var _secondParam = 'provider definition object';
:marked
The first is the [token](#token) that serves as the key for both locating a dependency value
and registering the provider.
The second is a !{_secondParam},
which we can think of as a *recipe* for creating the dependency value.
There are many ways to create dependency values ... and many ways to write a recipe.
#class-provider
:marked
### Alternative class providers
Occasionally we'll ask a different class to provide the service.
The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`.
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
block dart-diff-const-metadata
//- N/A
:marked
### Class provider with dependencies
Maybe an `EvenBetterLogger` could display the user name in the log message.
This logger gets the user from the injected `UserService`,
which happens also to be injected at the application level.
+makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')(format='.')
:marked
Configure it like we did `BetterLogger`.
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".")
:marked
### Aliased class providers
Suppose an old component depends upon an `OldLogger` class.
`OldLogger` has the same interface as the `NewLogger`, but for some reason
we can't update the old component to use it.
When the *old* component logs a message with `OldLogger`,
we want the singleton instance of `NewLogger` to handle it instead.
The dependency injector should inject that singleton instance
when a component asks for either the new or the old logger.
The `OldLogger` should be an alias for `NewLogger`.
We certainly do not want two different `NewLogger` instances in our app.
Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`.
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".")
:marked
The solution: alias with the `useExisting` option.
- var stylePattern = { otl: /(useExisting: \w*)/gm };
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b', '', stylePattern)(format=".")
#value-provider
:marked
### Value providers
:marked
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
block dart-diff-const-metadata-ctor
//- N/A
+makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
:marked
Then we register a provider with the `useValue` option,
which makes this object play the logger role.
- var stylePattern = { otl: /(useValue: \w*)/gm };
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-7', '', stylePattern)(format=".")
:marked
See more `useValue` examples in the
[Non-class dependencies](#non-class-dependencies) and
[OpaqueToken](#opaquetoken) sections.
#factory-provider
:marked
### Factory providers
Sometimes we need to create the dependent value dynamically,
based on information we won't have until the last possible moment.
Maybe the information changes repeatedly in the course of the browser session.
Suppose also that the injectable service has no independent access to the source of this information.
This situation calls for a **factory provider**.
Let's illustrate by adding a new business requirement:
the HeroService must hide *secret* heroes from normal users.
Only authorized users should see secret heroes.
Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user.
It needs to know if the user is authorized to see secret heroes.
That authorization can change during the course of a single application session,
as when we log in a different user.
Unlike `EvenBetterLogger`, we can't inject the `UserService` into the `HeroService`.
The `HeroService` won't have direct access to the user information to decide
who is authorized and who is not.
.l-sub-section
:marked
Why? We don't know either. Stuff like this happens.
:marked
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (excerpt)')(format='.')
:marked
We can inject the `Logger`, but we can't inject the boolean `isAuthorized`.
We'll have to take over the creation of new instances of this `HeroService` with a factory provider.
A factory provider needs a factory function:
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
:marked
Although the `HeroService` has no access to the `UserService`, our factory function does.
We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function:
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
.l-sub-section
:marked
The `useFactory` field tells Angular that the provider is a factory function
whose implementation is the `heroServiceFactory`.
The `deps` property is #{_an} #{_array} of [provider tokens](#token).
The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
- var exportedvar = _docsFor == 'dart' ? 'constant' : 'exported variable'
- var variable = _docsFor == 'dart' ? 'constant' : 'variable'
:marked
Notice that we captured the factory provider in #{_an} #{exportedvar}, `heroServiceProvider`.
This extra step makes the factory provider reusable.
We can register our `HeroService` with this #{variable} wherever we need it.
In our sample, we need it only in the `HeroesComponent`,
where it replaces the previous `HeroService` registration in the metadata `providers` #{_array}.
Here we see the new and the old implementation side-by-side:
- var stylePattern = { otl: /(providers.*),$/gm };
+makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.ts,
dependency-injection/ts/app/heroes/heroes.component.1.ts`,
',full',
`app/heroes/heroes.component (v3),
app/heroes/heroes.component (v2)`,
stylePattern)
.l-main-section#token
:marked
## Dependency injection tokens
When we register a provider with an injector, we associate that provider with a dependency injection token.
The injector maintains an internal *token-provider* map that it references when
asked for a dependency. The token is the key to the map.
In all previous examples, the dependency value has been a class *instance*, and
the class *type* served as its own lookup key.
Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
:marked
We have similar good fortune when we write a constructor that requires an injected class-based dependency.
We define a constructor parameter with the `HeroService` class type,
and Angular knows to inject the
service associated with that `HeroService` class token:
+makeExample('dependency-injection/ts/app/heroes/hero-list.component.ts', 'ctor-signature')
:marked
This is especially convenient when we consider that most dependency values are provided by classes.
//- TODO: if function injection is useful explain or illustrate why.
:marked
### Non-class dependencies
p
| What if the dependency value isn't a class? Sometimes the thing we want to inject is a
block non-class-dep-eg
span string, function, or object.
p
| Applications often define configuration objects with lots of small facts
| (like the title of the application or the address of a web API endpoint)
block config-obj-maps
| &nbsp;but these configuration objects aren't always instances of a class.
| They can be object literals
| &nbsp;such as this one:
+makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.')
:marked
We'd like to make this configuration object available for injection.
We know we can register an object with a [value provider](#value-provider).
block what-should-we-use-as-token
:marked
But what should we use as the token?
We don't have a class to serve as a token.
There is no `AppConfig` class.
.l-sub-section#interface
:marked
### TypeScript interfaces aren't valid tokens
The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, we
cannot use a TypeScript interface as a token:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9-interface')(format=".")
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9-ctor-interface')(format=".")
:marked
That seems strange if we're used to dependency injection in strongly typed languages, where
an interface is the preferred dependency lookup key.
It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces.
The TypeScript interface disappears from the generated JavaScript.
There is no interface type information left for Angular to find at runtime.
//- FIXME simplify once APIs are defined for Dart.
- var opaquetoken = _docsFor == 'dart' ? '<b>OpaqueToken</b>' : '<a href="../api/core/index/OpaqueToken-class.html"><b>OpaqueToken</b></a>'
:marked
### OpaqueToken
One solution to choosing a provider token for non-class dependencies is
to define and use an !{opaquetoken}.
The definition looks like this:
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
:marked
We register the dependency provider using the `OpaqueToken` object:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9')(format=".")
:marked
Now we can inject the configuration object into any constructor that needs it, with
the help of an `@Inject` #{_decorator}:
+makeExample('dependency-injection/ts/app/app.component.2.ts','ctor')(format=".")
- var configType = _docsFor == 'dart' ? '<code>Map</code>' : '<code>AppConfig</code>'
.l-sub-section
:marked
Although the !{configType} interface plays no role in dependency injection,
it supports typing of the configuration object within the class.
block dart-map-alternative
:marked
Or we can provide and inject the configuration object in our top-level `AppComponent`.
+makeExcerpt('app/app.component.ts','providers')
#optional
:marked
## Optional dependencies
Our `HeroService` *requires* a `Logger`, but what if it could get by without
a logger?
We can tell Angular that the dependency is optional by annotating the
constructor argument with `@Optional()`:
+ifDocsFor('ts')
+makeExample('dependency-injection/ts/app/providers.component.ts','import-optional', '')
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor', '')(format='.')
:marked
When using `@Optional()`, our code must be prepared for a null value. If we
don't register a logger somewhere up the line, the injector will set the
value of `logger` to null.
.l-main-section
:marked
## Summary
We learned the basics of Angular dependency injection in this chapter.
We can register various kinds of providers,
and we know how to ask for an injected object (such as a service) by
adding a parameter to a constructor.
Angular dependency injection is more capable than we've described.
We can learn more about its advanced features, beginning with its support for
nested injectors, in the
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
.l-main-section#explicit-injector
:marked
## Appendix: Working with injectors directly
We rarely work directly with an injector, but
here's an `InjectorComponent` that does.
+makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts')
:marked
An `Injector` is itself an injectable service.
In this example, Angular injects the component's own `Injector` into the component's constructor.
The component then asks the injected injector for the services it wants.
Note that the services themselves are not injected into the component.
They are retrieved by calling `injector.get`.
The `get` method throws an error if it can't resolve the requested service.
We can call `get` with a second parameter (the value to return if the service is not found)
instead, which we do in one case
to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector.
.l-sub-section
:marked
The technique we just described is an example of the
[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
We **avoid** this technique unless we genuinely need it.
It encourages a careless grab-bag approach such as we see here.
It's difficult to explain, understand, and test.
We can't know by inspecting the constructor what this class requires or what it will do.
It could acquire services from any ancestor component, not just its own.
We're forced to spelunk the implementation to discover what it does.
Framework developers may take this approach when they
must acquire services generically and dynamically.
+ifDocsFor('ts')
.l-main-section#one-class-per-file
:marked
## Appendix: Why we recommend one class per file
Having multiple classes in the same file is confusing and best avoided.
Developers expect one class per file. Keep them happy.
If we scorn this advice and, say,
combine our `HeroService` class with the `HeroesComponent` in the same file,
**define the component last!**
If we define the component before the service,
we'll get a runtime null reference error.
.l-sub-section
:marked
We actually can define the component first with the help of the `forwardRef()` method as explained
in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
But why flirt with trouble?
Avoid the problem altogether by defining components and services in separate files.

View File

@ -0,0 +1,283 @@
block includes
include ../_util-fns
- var _iterableUrl = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols';
- var _boolean = 'truthy/falsey';
:marked
We typically display data in Angular by binding controls in an HTML template
to properties of an Angular component.
In this chapter, we'll create a component with a list of heroes. Each hero has a name.
We'll display the list of hero names and
conditionally show a message below the list.
The final UI looks like this:
figure.image-display
img(src="/resources/images/devguide/displaying-data/final.png" alt="Final UI")
:marked
# Table Of Contents
* [Showing component properties with interpolation](#interpolation)
* [Showing !{_an} !{_array} property with NgFor](#ngFor)
* [Conditional display with NgIf](#ngIf)
.l-sub-section
:marked
The <live-example></live-example> demonstrates all of the syntax and code
snippets described in this chapter.
.l-main-section#interpolation
:marked
## Showing component properties with interpolation
The easiest way to display a component property
is to bind the property name through interpolation.
With interpolation, we put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
Let's build a small illustrative example together.
Create a new project folder (<ngio-ex path="displaying-data"></ngio-ex>) and follow the steps in the [QuickStart](../quickstart.html).
block quickstart-repo
include ../_quickstart_repo
:marked
Then modify the <ngio-ex path="app.component.ts"></ngio-ex> file by
changing the template and the body of the component.
When we're done, it should look like this:
+makeExample('app/app.component.1.ts')
:marked
We added two properties to the formerly empty component: `title` and `myHero`.
Our revised template displays the two component properties using double curly brace
interpolation:
+makeExcerpt('app/app.component.1.ts', 'template', '')
+ifDocsFor('ts')
.l-sub-section
:marked
The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>).
The backtick (<code>\`</code>) &mdash; which is *not* the same character as a single
quote (`'`) &mdash; has many nice features. The feature we're exploiting here
is the ability to compose the string over several lines, which makes for
much more readable HTML.
:marked
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
inserts those values into the browser. Angular updates the display
when these properties change.
.l-sub-section
:marked
More precisely, the redisplay occurs after some kind of asynchronous event related to
the view such as a keystroke, a timer completion, or an async `XHR` response.
We don't have those in this sample.
But then the properties aren't changing on their own either. For the moment we must operate on faith.
:marked
Notice that we haven't called **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for us. How?
Notice the CSS `selector` in the `@Component` !{_decorator} that specifies an element named `my-app`.
Remember back in [QuickStart](../quickstart.html) that we added the `<my-app>` element to the body of our `index.html` file:
+makeExcerpt('index.html', 'body')
:marked
When we bootstrap with the `AppComponent` class (in <ngio-ex path="main.ts"></ngio-ex>), Angular looks for a `<my-app>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag.
Try running the app. It should display the title and hero name:
figure.image-display
img(src="/resources/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero")
+ifDocsFor('ts')
:marked
Let's review some of the choices we made and consider alternatives.
:marked
## Template inline or template file?
We can store our component's template in one of two places.
We can define it *inline* using the `template` property, as we do here.
Or we can define the template in a separate HTML file and link to it in
the component metadata using the `@Component` !{_decorator}'s `templateUrl` property.
The choice between inline and separate HTML is a matter of taste,
circumstances, and organization policy.
Here we're using inline HTML because the template is small, and the demo
is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties.
+ifDocsFor('ts')
:marked
## Constructor or variable initialization?
We initialized our component properties using variable assignment.
This is a wonderfully concise and compact technique.
Some folks prefer to declare the properties and initialize them within a constructor like this:
+makeExcerpt('app/app-ctor.component.ts', 'class')
:marked
That's fine too. The choice is a matter of taste and organization policy.
We'll adopt the more terse "variable assignment" style in this chapter simply because
there will be less code to read.
.l-main-section#ngFor
:marked
## Showing !{_an} !{_array} property with ***ngFor**
We want to display a list of heroes. We begin by adding !{_an} !{_array} of hero names to the component and redefine `myHero` to be the first name in the !{_array}.
+makeExcerpt('app/app.component.2.ts', 'class')
:marked
Now we use the Angular `ngFor` directive in the template to display
each item in the `heroes` list.
+makeExcerpt('app/app.component.2.ts', 'template')
:marked
Our presentation is the familiar HTML unordered list with `<ul>` and `<li>` tags. Let's focus on the `<li>` tag.
+makeExcerpt('app/app.component.2.ts ()', 'li', '')
:marked
We added a somewhat mysterious `*ngFor` to the `<li>` element.
That's the Angular "repeater" directive.
Its presence on the `<li>` tag marks that `<li>` element (and its children) as the "repeater template".
.alert.is-important
:marked
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
Learn more about this and `ngFor` in the [Template Syntax](./template-syntax.html#ngFor) chapter.
:marked
Notice the `hero` in the `ngFor` double-quoted instruction;
it is an example of a [template input variable](./template-syntax.html#ngForMicrosyntax).
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable
to the item (the hero) in the current iteration. Angular uses that variable as the
context for the interpolation in the double curly braces.
.l-sub-section
:marked
We happened to give `ngFor` !{_an} !{_array} to display.
In fact, `ngFor` can repeat items for any [iterable](!{_iterableUrl})
object.
:marked
Now the heroes appear in an unordered list.
figure.image-display
img(src="/resources/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor")
.l-main-section
:marked
## Creating a class for the data
We are defining our data directly inside our component.
That's fine for a demo but certainly isn't a best practice. It's not even a good practice.
Although we won't do anything about that in this chapter, we'll make a mental note to fix this down the road.
At the moment, we're binding to !{_an} !{_array} of strings. We do that occasionally in real applications, but
most of the time we're binding to more specialized objects.
Let's turn our !{_array} of hero names into !{_an} !{_array} of `Hero` objects. For that we'll need a `Hero` class.
Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code:
+makeExcerpt('app/hero.ts')
block hero-class
:marked
We've defined a class with a constructor and two properties: `id` and `name`.
It might not look like we have properties, but we do. We're taking
advantage of a TypeScript shortcut in our declaration of the constructor parameters.
Consider the first parameter:
+makeExcerpt('app/hero.ts ()', 'id')
:marked
That brief syntax does a lot:
* Declares a constructor parameter and its type
* Declares a public property of the same name
* Initializes that property with the corresponding argument when we "new" an instance of the class
.l-main-section
:marked
## Using the Hero class
Let's make the `heroes` property in our component return !{_an} !{_array} of these `Hero` objects.
+makeExcerpt('app/app.component.3.ts', 'heroes')
:marked
We'll have to update the template.
At the moment it displays the hero's `id` and `name`.
Let's fix that so we display only the hero's `name` property.
+makeExcerpt('app/app.component.3.ts', 'template')
:marked
Our display looks the same, but now we know much better what a hero really is.
.l-main-section#ngIf
:marked
## Conditional display with NgIf
Sometimes an app needs to display a view or a portion of a view only under specific circumstances.
In our example, we'd like to display a message if we have a large number of heroes, say, more than 3.
The Angular `ngIf` directive inserts or removes an element based on a !{_boolean} condition.
We can see it in action by adding the following paragraph at the bottom of the template:
+makeExcerpt('app/app.component.ts', 'message')
.alert.is-important
:marked
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
Learn more about this and `ngIf` in the [Template Syntax](./template-syntax.html#ngIf) chapter.
:marked
The [template expression](./template-syntax.html#template-expressions) inside the double quotes
looks much like !{_Lang}, and it _is_ much like !{_Lang}.
When the component's list of heroes has more than 3 items, Angular adds the paragraph to the DOM and the message appears.
If there are 3 or fewer items, Angular omits the paragraph, so no message appears.
.alert.is-helpful
:marked
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM.
That hardly matters here. But it would matter a great deal, from a performance perspective, if
we were conditionally including or excluding a big chunk of HTML with many data bindings.
:marked
Try it out. Because the !{_array} has four items, the message should appear.
Go back into <ngio-ex path="app.component.ts"></ngio-ex> and delete or comment out one of the elements from the hero !{_array}.
The browser should refresh automatically and the message should disappear.
.l-main-section
:marked
## Summary
Now we know how to use:
- **Interpolation** with double curly braces to display a component property
- **ngFor** to display !{_an} !{_array} of items
- A !{_Lang} class to shape the **model data** for our component and display properties of that model
- **ngIf** to conditionally display a chunk of HTML based on a boolean expression
Here's our final code:
block final-code
+makeTabs(`displaying-data/ts/app/app.component.ts,
displaying-data/ts/app/hero.ts,
displaying-data/ts/app/main.ts`,
'final,,',
'app/app.component.ts, app/hero.ts, main.ts')

View File

@ -0,0 +1,178 @@
block includes
include ../_util-fns
:marked
We learned the basics of Angular Dependency injection in the
[Dependency Injection](./dependency-injection.html) chapter.
Angular has a Hierarchical Dependency Injection system.
There is actually a tree of injectors
that parallel an application's component tree.
We can re-configure the injectors at any level of that component tree with
interesting and useful results.
In this chapter we explore these points and write some code.
Try the <live-example></live-example>.
.l-main-section
:marked
## The Injector Tree
In the [Dependency Injection](./dependency-injection.html) chapter
we learned how to configure a dependency injector and how to retrieve dependencies where we need them.
We oversimplified. In fact, there is no such thing as ***the*** injector!
An application may have multiple injectors!
An Angular application is a tree of components. Each component instance has its own injector!
The tree of components parallels the tree of injectors.
.l-sub-section
:marked
Angular doesn't *literally* create a separate injector for each component.
Every component doesn't need its own injector and it would be horribly inefficient to create
masses of injectors for no good purpose.
But it is true that every component ***has an injector*** (even if it shares that injector with another component)
and there may be many different injector instances operating at different levels of the component tree.
It is useful to pretend that every component has its own injector.
:marked
Consider a simple variation on the Tour of Heroes application consisting of three different components:
`HeroesApp`, `HeroesListComponent` and `HeroesCardComponent`.
The `HeroesApp` holds a single instance of `HeroesListComponent`.
The new twist is that the `HeroesListComponent` may hold and manage multiple instances of the `HeroesCardComponent`.
The following diagram represents the state of the component tree when there are three instances of `HeroesCardComponent`
open simultaneously.
figure.image-display
img(src="/resources/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="500")
:marked
Each component instance gets its own injector and an injector at one level is a child injector of the injector above it in the tree.
When a component at the bottom requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector.
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
If that injector can't satisfy the request, it passes it along to *its* parent component's injector.
The requests keep bubbling up until we find an injector that can handle the request or run out of component ancestors.
If we run out of ancestors, Angular throws an error.
.l-sub-section
:marked
There's a third possibility. An intermediate component can declare that it is the "host" component.
The hunt for providers will climb no higher than the injector for this host component.
We'll reserve discussion of this option for another day.
:marked
Such a proliferation of injectors makes little sense until we consider the possibility that injectors at different levels can be
configured with different providers. We don't *have* to re-configure providers at every level. But we *can*.
If we don't re-configure, the tree of injectors appears to be flat. All requests bubble up to the root injector that we
configured with the `bootstrap` method.
The ability to configure one or more providers at different levels opens up interesting and useful possibilities.
Lets return to our Car example.
Suppose we configured the root injector (marked as A) with providers for `Car`, `Engine` and `Tires`.
We create a child component (B) that defines its own providers for `Car` and `Engine`
This child is the parent of another component (C) that defines its own provider for `Car`.
Behind the scenes each component sets up its own injector with one or more providers defined for that component itself.
When we resolve an instance of `Car` at the deepest component (C),
its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and
`Tires` resolved by the root injector (A).
figure.image-display
img(src="/resources/images/devguide/dependency-injection/injector-tree.png" alt="injector tree" width="600")
.l-main-section
:marked
## Component Injectors
In the previous section, we talked about injectors and how they are organized like a tree. Lookups follow the injector tree upwards until they find the requested thing to inject. But when do we actually want to provide providers on the root injector and when do we want to provide them on a child injector?
Consider you are building a component to show a list of super heroes that displays each super hero in a card with its name and superpower. There should also be an edit button that opens up an editor to change the name and superpower of our hero.
One important aspect of the editing functionality is that we want to allow multiple heroes to be in edit mode at the same time and that one can always either commit or cancel the proposed changes.
Lets take a look at the `HeroesListComponent` which is the root component for this example.
+makeExample('hierarchical-dependency-injection/ts/app/heroes-list.component.ts', null, 'app/heroes-list.component.ts')
:marked
Notice that it imports the `HeroService` that weve used before so we can skip its declaration. The only difference is that weve used a more formal approach for our `Hero`model and defined it upfront as such.
+makeExample('hierarchical-dependency-injection/ts/app/hero.ts', null, 'app/hero.ts')(format=".")
:marked
Our `HeroesListComponent` defines a template that creates a list of `HeroCardComponent`s and `HeroEditorComponent`s, each bound to an instance of hero that is returned from the `HeroService`. Ok, thats not entirely true. It actually binds to an `EditItem<Hero>` which is a simple generic datatype that can wrap any type and indicate if the item being wrapped is currently being edited or not.
+makeExample('hierarchical-dependency-injection/ts/app/edit-item.ts', null, 'app/edit-item.ts')(format=".")
:marked
But how is `HeroCardComponent` implemented? Lets take a look.
+makeExample('hierarchical-dependency-injection/ts/app/hero-card.component.ts', null, 'app/hero-card.component.ts')
:marked
The `HeroCardComponent` is basically a component that defines a template to render a hero. Nothing more.
Lets get to the interesting part and take a look at the `HeroEditorComponent`
+makeExample('hierarchical-dependency-injection/ts/app/hero-editor.component.ts', null, 'app/hero-editor.component.ts')
:marked
Now here its getting interesting. The `HeroEditorComponent`defines a template with an input to change the name of the hero and a `cancel` and a `save` button. Remember that we said we want to have the flexibility to cancel our editing and restore the old value? This means we need to maintain two copies of our `Hero` that we want to edit. Thinking ahead, this is a perfect use case to abstract it into its own generic service since we have probably more cases like this in our app.
And this is where the `RestoreService` enters the stage.
+makeExample('hierarchical-dependency-injection/ts/app/restore.service.ts', null, 'app/restore.service.ts')
:marked
All this tiny service does is define an API to set a value of any type which can be altered, retrieved or set back to its initial value. Thats exactly what we need to implement the desired functionality.
Our `HeroEditComponent` uses this services under the hood for its `hero` property. It intercepts the `get` and `set` method to delegate the actual work to our `RestoreService` which in turn makes sure that we wont work on the original item but on a copy instead.
At this point we may be scratching our heads asking what this has to do with component injectors?
Look closely at the metadata for our `HeroEditComponent`. Notice the `providers` property.
+makeExample('hierarchical-dependency-injection/ts/app/hero-editor.component.ts', 'providers')
:marked
This adds a `RestoreService` provider to the injector of the `HeroEditComponent`.
Couldnt we simply alter our bootstrap call to this?
+makeExample('hierarchical-dependency-injection/ts/app/main.ts', 'bad-alternative')
:marked
Technically we could, but our component wouldnt quite behave the way it is supposed to. Remember that each injector treats the services that it provides as singletons. However, in order to be able to have multiple instances of `HeroEditComponent` edit multiple heroes at the same time we need to have multiple instances of the `RestoreService`. More specifically, each instance of `HeroEditComponent` needs to be bound to its own instance of the `RestoreService`.
By configuring a provider for the `RestoreService` on the `HeroEditComponent`, we get exactly one new instance of the `RestoreService`per `HeroEditComponent`.
Does that mean that services arent singletons anymore in Angular 2? Yes and no.
There can be only one instance of a service type in a particular injector.
But we've learned that we can have multiple injectors operating at different levels of the application's component tree.
Any of those injectors could have its own instance of the service.
If we defined a `RestoreService` provider only on the root component,
we would have exactly one instance of that service and it would be shared across the entire application.
Thats clearly not what we want in this scenario. We want each component to have its own instance of the `RestoreService`.
Defining (or re-defining) a provider at the component level creates a new instance of the service for each new instance
of that component. We've made the `RestoreService` a kind of "private" singleton for each `HeroEditComponent`,
scoped to that component instance and its child components.
<!--
## Advanced Dependency Injection in Angular 2
Restrict Dependency Lookups
[TODO] (@Host) This has been postponed for now until we come up with a decent use case
.l-main-section
:marked
## Dependency Visibility
[TODO] (providers vs viewProviders) This has been postponed for now until come up with a decent use case
-->

View File

@ -0,0 +1,574 @@
block includes
include ../_util-fns
- var top="vertical-align:top"
:marked
# Component Lifecycle
A Component has a lifecycle managed by Angular itself. 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.
Angular offers **component lifecycle hooks**
that give us visibility into these key moments and the ability to act when they occur.
We cover these hooks in this chapter and demonstrate how they work in code.
* [The lifecycle hooks](#hooks-overview)
* [The hook-call sequence](#hook-sequence)
* [Other Angular lifecycle hooks](#other-lifecycles)
* [The lifecycle sample](#the-sample)
* [All](#peek-a-boo)
* [Spying OnInit and OnDestroy](#spy)
* [OnChanges](#onchanges)
* [DoCheck](#docheck)
* [AfterViewInit and AfterViewChecked](#afterview)
* [AfterContentInit and AfterContentChecked](#aftercontent)
Try the <live-example></live-example>.
a#hooks-overview
.l-main-section
:marked
## Component lifecycle Hooks
Directive and component instances have a lifecycle
as Angular creates, updates, and destroys them.
Developers can tap into key moments in that lifecycle by implementing
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`.
We might implement it in a component class like this:
+makeExample('lifecycle-hooks/ts/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.')
:marked
No directive or component will implement all of them and some of the hooks only make sense for components.
Angular only calls a directive/component hook method *if it is defined*.
block optional-interfaces
.l-sub-section
:marked
### Interface optional?
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
The JavaScript language doesn't have interfaces.
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
Fortunately, they aren't necessary.
We don't have to add the lifecycle hook interfaces to our directives and components to benefit from the hooks themselves.
Angular instead inspects our directive and component classes and calls the hook methods *if they are defined*.
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
in order to benefit from strong typing and editor tooling.
:marked
Here are the component lifecycle hook methods:
### Directives and Components
table(width="100%")
col(width="20%")
col(width="80%")
tr
th Hook
th Purpose
tr(style=top)
td ngOnInit
td
:marked
Initialize the directive/component after Angular initializes the data-bound input properties.
tr(style=top)
td ngOnChanges
td
:marked
Respond after Angular sets a data-bound input property.
The method receives a `changes` object of current and previous values.
tr(style=top)
td ngDoCheck
td
:marked
Detect and act upon changes that Angular can or won't
detect on its own. Called every change detection run.
tr(style=top)
td ngOnDestroy
td
:marked
Cleanup just before Angular destroys the directive/component.
Unsubscribe observables and detach event handlers to avoid memory leaks.
:marked
### Components only
table(width="100%")
col(width="20%")
col(width="80%")
tr
th Hook
th Purpose
tr(style=top)
td ngAfterContentInit
td
:marked
After Angular projects external content into its view.
tr(style=top)
td ngAfterContentChecked
td
:marked
After Angular checks the bindings of the external content that it projected into its view.
tr(style=top)
td ngAfterViewInit
td
:marked
After Angular creates the component's view(s).
tr(style=top)
td ngAfterViewChecked
td
:marked
After Angular checks the bindings of the component's view(s).
:marked
Angular does not call the hook methods in this order.
a(id="hook-sequence")
.l-main-section
:marked
## Lifecycle sequence
*After* Angular creates a component/directive by `new`-ing its constructor,
it calls the lifecycle hook methods in the following sequence at specific moments:
table(width="100%")
col(width="20%")
col(width="80%")
tr
th Hook
th Timing
tr(style=top)
td ngOnChanges
td
:marked
before `ngOnInit` and when a data-bound input property value changes.
tr(style=top)
td ngOnInit
td
:marked
after the first `ngOnChanges`.
tr(style=top)
td ngDoCheck
td
:marked
during every Angular change detection cycle.
tr(style=top)
td ngAfterContentInit
td
:marked
after projecting content into the component.
tr(style=top)
td ngAfterContentChecked
td
:marked
after every check of projected component content.
tr(style=top)
td ngAfterViewInit
td
:marked
after initializing the component's views and child views.
tr(style=top)
td ngAfterViewChecked
td
:marked
after every check of the component's views and child views.
tr(style=top)
td ngOnDestroy
td
:marked
just before Angular destroys the directive/component.
a(id="other-lifecycles")
.l-main-section
:marked
## Other lifecycle hooks
Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed.
The router, for instance, also has it's own [router lifecycle hooks](router.html#router-lifecycle-hooks)
that allow us to tap into specific moments in route navigation.
A parallel can be drawn between `ngOnInit` and `routerOnActivate`.
Both are prefixed so as to avoid collision, and both run right when a component is 'booting' up.
3rd party libraries might implement their hooks as well in order to give us, the developers, more
control over how these libraries are used.
.l-main-section#the-sample
:marked
## Lifecycle exercises
The <live-example></live-example>
demonstrates the lifecycle hooks in action through a series of exercises
presented as components under the control of the root `AppComponent`.
They follow a common pattern: a *parent* component serves as a test rig for
a *child* component that illustrates one or more of the lifecycle hook methods.
Here's a brief description of each exercise:
table(width="100%")
col(width="20%")
col(width="80%")
tr
th Component
th Description
tr(style=top)
td <a href="#peek-a-boo">Peek-a-boo</a>
td
:marked
Demonstrates every lifecycle hook.
Each hook method writes to the on-screen log.
tr(style=top)
td <a href="#spy">Spy</a>
td
:marked
Directives have lifecycle hooks too.
We create a `SpyDirective` that logs when the element it spies upon is
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
managed by the parent `SpyComponent`.
tr(style=top)
td <a href="#onchanges">OnChanges</a>
td
:marked
See how Angular calls the `ngOnChanges` hook with a `changes` object
every time one of the component input properties changes.
Shows how to interpret the `changes` object.
tr(style=top)
td <a href="#docheck">DoCheck</a>
td
:marked
Implements an `ngDoCheck` method with custom change detection.
See how often Angular calls this hook and watch it post changes to a log.
tr(style=top)
td <a href="#afterview">AfterView</a>
td
:marked
Shows what Angular means by a *view*.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
tr(style=top)
td <a href="#aftercontent">AfterContent</a>
td
:marked
Shows how to project external content into a component and
how to distinguish projected content from a component's view children.
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
tr(style=top)
td Counter
td
:marked
Demonstrates a combination of a component and a directive
each with its own hooks.
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
every time the parent component increments its input counter property.
Meanwhile, we apply the `SpyDirective` from the previous example
to the `CounterComponent` log and watch log entries be created and destroyed.
:marked
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
a(id="peek-a-boo")
.l-main-section
:marked
## Peek-a-boo: all hooks
The `PeekABooComponent` demonstrates all of the hooks in one component.
In real life, we'd rarely if ever implement all of the interfaces like this.
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
figure.image-display
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
:marked
The sequence of log messages follows the prescribed hook calling order:
`OnChanges`, `OnInit`, `DoCheck`&nbsp;(3x), `AfterContentInit`, `AfterContentChecked`&nbsp;(3x),
`AfterViewInit`, `AfterViewChecked`&nbsp;(3x), and `OnDestroy`.
.l-sub-section
:marked
The constructor isn't an Angular hook *per se*.
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
:marked
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
as lean as possible!
Our next examples focus on hook details.
.a(id="spy")
.l-main-section
:marked
## Spying *OnInit* and *OnDestroy*
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
but we don't want *it* to know we're watching.
This is the perfect infiltration job for a directive.
Our heroes will never know it's there.
.l-sub-section
:marked
Kidding aside, we're emphasizing two key points:
1. Angular calls hook methods for *directives* as well as components.
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
Obviously we can't change the implementation of a native `div`.
We can't modify a third party component either.
But we can watch both with a directive.
:marked
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
that log messages to the parent via an injected `LoggerService`.
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
:marked
We can apply the spy to any native or component element and it'll be initialized and destroyed
at the same time as that element.
Here we attach it to the repeated hero `<div>`
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
:marked
Each spy's birth and death marks the birth and death of the attached hero `<div>`
with an entry in the *Hook Log* as we see here:
figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
:marked
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
We see a new entry for each hero.
The *Reset* button clears the `heroes` list.
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
The spy's `ngOnDestroy` method reports its last moments.
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
Let's see why we need them.
### OnInit
We turn to `ngOnInit` for two main reasons:
1. To perform complex initializations shortly after construction
1. To set up the component after Angular sets the input properties
An `ngOnInit` often fetches data for the component as shown in the
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
We don't fetch data in a component constructor. Why?
Because experienced developers agree that components should be cheap and safe to construct.
We shouldn't worry that a new component will try to contact a remote server when
created under test or before we decide to display it.
Constructors should do no more than set the initial local variables to simple values.
When a component must start working _soon_ after creation,
we can count on Angular to call the `ngOnInit` method to jumpstart it.
That's where the heavy initialization logic belongs.
Remember also that a directive's data-bound input properties are not set until _after construction_.
That's a problem if we need to initialize the directive based on those properties.
They'll have been set when our `ngOninit` runs.
.l-sub-section
:marked
Our first opportunity to access those properties is the `ngOnChanges` method which
Angular calls before `ngOnit`. But Angular calls `ngOnChanges` many times after that.
It only calls `ngOnit` once.
:marked
### OnDestroy
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
This is the time to notify another part of the application that this component is going away.
This is the place to free resources that won't be garbage collected automatically.
Unsubscribe from observables and DOM events. Stop interval timers.
Unregister all callbacks that this directive registered with global or application services.
We risk memory leaks if we neglect to do so.
.l-main-section
:marked
## OnChanges
We monitor the `OnChanges` hook in this example.
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
Here is our implementation of the hook.
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
:marked
The `ngOnChanges` method takes an object that maps each changed property name to a
[SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values.
We iterate over the changed properties and log them.
The input properties for our example `OnChangesComponent` are `hero` and `power`.
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
:marked
The parent binds to them like this:
+makeExample('lifecycle-hooks/ts/app/on-changes-parent.component.html', 'on-changes')
:marked
Here's the sample in action as we make changes.
figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
:marked
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
That's surprising at first.
Angular only calls the hook when the value of the input property changes.
The value of the `hero` property is the *reference to the hero object*.
Angular doesn't care that the hero's own `name` property changed.
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
.l-main-section
:marked
## DoCheck
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
.l-sub-section
:marked
With this method we can detect a change that Angular overlooked.
What we do with that information to refresh the display is a separate matter.
:marked
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
:marked
We manually check everything that we care about, capturing and comparing against previous values.
We write a special message to the log when there are no substantive changes
to the hero or the power so we can keep an eye on the method's performance characteristics.
The results are illuminating:
figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
:marked
We now are able to detect when the hero's `name` has changed. But we must be careful.
The `ngDoCheck` 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.
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
Mere mousing into another input box triggers a call.
Relatively few calls reveal actual changes to pertinent data.
Clearly our implementation must be very lightweight or the user experience may suffer.
.l-sub-section
:marked
We also see that the `ngOnChanges` method is called in contradiction of the
[incorrect API documentation](../api/core/index/DoCheck-class.html).
.l-main-section
:marked
## AfterView
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
*after* it creates a component's child views.
Here's a child view that displays a hero's name in an input box:
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
:marked
The `AfterViewComponent` displays this child view *within its template*:
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
:marked
The following hooks take action based on changing values *within the child view*
which we can only reach by querying for the child view via the property decorated with
[@ViewChild](../api/core/index/ViewChild-var.html).
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
.a(id="wait-a-tick")
:marked
### Abide by the unidirectional data flow rule
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
:marked
Why does the `doSomething` method wait a tick before updating `comment`?
Because we must adhere to Angular's unidirectional data flow rule which says that
we may not update the view *after* it has been composed.
Both hooks fire after the component's view has been composed.
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
block tick-methods
:marked
The `LoggerService.tick` methods, which are implemented by a call to `setTimeout`, postpone the update one turn of the of the browser's JavaScript cycle ... and that's long enough.
:marked
Here's *AfterView* in action
figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView")
:marked
Notice that Angular frequently calls `AfterViewChecked`, often when there are no changes of interest.
Write lean hook methods to avoid performance problems.
.l-main-section
:marked
## AfterContent
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
*after* Angular projects external content into the component.
### Content projection
*Content projection* is a way to import HTML content from outside the component and insert that content
into the component's template in a designated spot.
.l-sub-section
:marked
Angular 1 developers know this technique as *transclusion*.
:marked
We'll illustrate with a variation on the [previous](#afterview) example
whose behavior and output is almost the same.
This time, instead of including the child view within the template, we'll import it from
the `AfterContentComponent`'s parent. Here's the parent's template.
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
:marked
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
We never put content between a component's element tags *unless we intend to project that content
into the component*.
Now look at the component's template:
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
:marked
The `<ng-content>` tag is a *placeholder* for the external content.
They tell Angular where to insert that content.
In this case, the projected content is the `<my-child>` from the parent.
figure.image-display
img(src='/resources/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content")
:marked
.l-sub-section
:marked
The tell-tale signs of *content projection* are (a) HTML between component element tags
and (b) the presence of `<ng-content>` tags in the component's template.
:marked
### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
that we're looking for.
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
appear *within* the component's template.
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
projected into the component.
The following *AfterContent* hooks take action based on changing values in a *content child*
which we can only reach by querying for it via the property decorated with
[@ContentChild](../api/core/index/ContentChild-var.html).
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
:marked
### No unidirectional flow worries
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
There's no [need to wait](#wait-a-tick).
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
Angular completes composition of the projected content *before* finishing the composition of this component's view.
We still have a window of opportunity to modify that view.

View File

@ -0,0 +1,487 @@
block includes
include ../_util-fns
:marked
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
Getting data could be as simple as creating a local variable or as complex as streaming data over a Websocket.
Once data arrive, we could push their raw `toString` values directly to the view.
That rarely makes for a good user experience.
E.g., almost everyone prefers a simple birthday date like
<samp>April 15, 1988</samp> to the original raw string format
&mdash; <samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>.
Clearly some values benefit from a bit of massage. We soon discover that we
desire many of the same transformations repeatedly, both within and across many applications.
We almost think of them as styles.
In fact, we'd like to apply them in our HTML templates as we do styles.
Introducing Angular pipes, a way to write display-value transformations that we can declare in our HTML!
Try the <live-example></live-example>.
.l-main-section
:marked
## Using Pipes
A pipe takes in data as input and transforms it to a desired output.
We'll illustrate by transforming a component's birthday property into
a human-friendly date.
+makeExample('pipes/ts/app/hero-birthday1.component.ts', null, 'app/hero-birthday1.component.ts')(format='.')
:marked
Focus on the component's template.
+makeExample('pipes/ts/app/app.component.html', 'hero-birthday-template')(format=".")
:marked
Inside the interpolation expression we flow the component's `birthday` value through the
[pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/index/DatePipe-class.html)
function on the right. All pipes work this way.
.l-sub-section
:marked
The `Date` and `Currency` pipes need the **ECMAScript Internationalization API**.
Safari and other older browsers don't support it. We can add support with a polyfill.
code-example(language="html").
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt;
.l-main-section
:marked
## Built-in pipes
Angular comes with a stock of pipes such as
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
They are all immediately available for use in any template.
.l-sub-section
:marked
Learn more about these and many other built-in pipes in the [API Reference](../api/#!?apiFilter=pipe);
filter for entries that include the word "pipe".
Angular 2 doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in an [appendix below](#no-filter-pipe).
.l-main-section
:marked
## Parameterizing a Pipe
A pipe may accept any number of optional parameters to fine-tune its output.
We add parameters to a pipe by following the pipe name with a colon ( : ) and then the parameter value
(e.g., `currency:'EUR'`). If our pipe accepts multiple parameters, we separate the values with colons (e.g. `slice:1:5`)
We'll modify our birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday, it should render as **<samp>04/15/88</samp>**:
+makeExample('pipes/ts/app/app.component.html', 'format-birthday')(format=".")
:marked
The parameter value can be any valid
[template expression](./template-syntax.html#template-expressions)
such as a string literal or a component property.
In other words, we can control the format through a binding the same way we control the birthday value through a binding.
Let's write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component:
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'template', 'app/hero-birthday2.component.ts (template)')(format=".")
:marked
We also added a button to the template and bound its click event to the component's `toggleFormat()` method.
That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`).
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'class', 'app/hero-birthday2.component.ts (class)')(format='.')
:marked
As we click the button, the displayed date alternates between
"**<samp>04/15/1988</samp>**" and
"**<samp>Friday, April 15, 1988</samp>**".
figure.image-display
img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle")
:marked
.l-sub-section
:marked
Learn more about the `DatePipes` format options in the [API Docs](../api/common/index/DatePipe-class.html).
:marked
## Chaining pipes
We can chain pipes together in potentially useful combinations.
In the following example, we chain the birthday to the `DatePipe` and on to the `UpperCasePipe`
so we can display the birthday in uppercase. The following birthday displays as
**<samp>APR 15, 1988</samp>**.
+makeExample('pipes/ts/app/app.component.html', 'chained-birthday')(format=".")
:marked
This example &mdash; which displays **<samp>FRIDAY, APRIL 15, 1988</samp>** &mdash;
chains the same pipes as above, but passes in a parameter to `date` as well.
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday')(format=".")
.l-main-section
:marked
## Custom Pipes
We can write our own custom pipes.
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
+makeExample('pipes/ts/app/exponential-strength.pipe.ts', null, 'app/exponential-strength.pipe.ts')(format=".")
:marked
This pipe definition reveals several key points:
* A pipe is a class decorated with pipe metadata.
* The pipe class implements the `PipeTransform` interface's `transform` method that
accepts an input value followed by optional parameters and returns the transformed value.
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
Our pipe has one such parameter: the `exponent`.
* We tell Angular that this is a pipe by applying the
`@Pipe` #{_decorator} which we import from the core Angular library.
* The `@Pipe` #{_decorator} allows us to define the
pipe name that we'll use within template expressions. It must be a valid JavaScript identifier.
Our pipe's name is `exponentialStrength`.
.l-sub-section
:marked
### The *PipeTransform* Interface
The `transform` method is essential to a pipe.
The `PipeTransform` *interface* defines that method and guides both tooling and the compiler.
It is technically optional; Angular looks for and executes the `transform` method regardless.
:marked
Now we need a component to demonstrate our pipe.
+makeExample('pipes/ts/app/power-booster.component.ts',null,'app/power-booster.component.ts')(format='.')
figure.image-display
img(src='/resources/images/devguide/pipes/power-booster.png' alt="Power Booster")
:marked
Two things to note:
1. We use our custom pipe the same way we use the built-in pipes.
1. We must include our pipe in the `pipes` #{_array} of the `@Component` #{_decorator}.
.callout.is-helpful
header Remember the pipes #{_array}!
:marked
Angular reports an error if we neglect to list our custom pipe.
We didn't list the `DatePipe` in our previous example because all
Angular built-in pipes are pre-registered.
Custom pipes must be registered manually.
:marked
If we try the <live-example></live-example>,
we can probe its behavior by changing the value and the optional exponent in the template.
## Power Boost Calculator (extra-credit)
It's not much fun updating the template to test our custom pipe.
We could upgrade the example to a "Power Boost Calculator" that combines
our pipe and two-way data binding with `ngModel`.
+makeExample('pipes/ts/app/power-boost-calculator.component.ts', null, '/app/power-boost-calculator.component.ts')(format='.')
figure.image-display
img(src='/resources/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator")
.l-main-section
a#change-detection
:marked
## Pipes and Change Detection
Angular looks for changes to data-bound values through a *change detection* process that runs after every JavaScript event:
every keystroke, mouse move, timer tick, and server response. This could be expensive.
Angular strives to lower the cost whenever possible and appropriate.
Angular picks a simpler, faster change detection algorithm when we use a pipe. Let's see how.
### No pipe
The component in our next example uses the default, aggressive change detection strategy to monitor and update
its display of every hero in the `heroes` #{_array}. Here's the template:
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-1', 'app/flying-heroes.component.html (v1)')(format='.')
:marked
The companion component class provides heroes, adds new heroes into the #{_array}, and can reset the #{_array}.
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'v1', 'app/flying-heroes.component.ts (v1)')(format='.')
:marked
We can add a new hero and Angular updates the display when we do.
The `reset` button replaces `heroes` with a new #{_array} of the original heroes and Angular updates the display when we do.
If we added the ability to remove or change a hero, Angular would detect those changes too and update the display as well.
### Flying Heroes pipe
Let's add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-flying-heroes', 'app/flying-heroes.component.html (flyers)')(format='.')
:marked
Here's the `FlyingHeroesPipe` implementation which follows the pattern for custom pipes we saw earlier.
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pure', 'app/flying-heroes.pipe.ts')(format='.')
:marked
When we run the sample now we see odd behavior (try it in the <live-example></live-example>).
Every hero we add is a flying hero but none of them are displayed.
Although we're not getting the behavior we want, Angular isn't broken.
It's just using a different change detection algorithm &mdash; one that ignores changes to the list or any of its items.
Look at how we're adding a new hero:
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'push')(format='.')
:marked
We're adding the new hero into the `heroes` #{_array}. The reference to the #{_array} hasn't changed.
It's the same #{_array}. That's all Angular cares about. From its perspective, *same #{_array}, no change, no display update*.
We can fix that. Let's create a new #{_array} with the new hero appended and assign that to `heroes`.
This time Angular detects that the #{_array} reference has changed.
It executes the pipe and updates the display with the new #{_array} which includes the new flying hero.
*If we **mutate** the #{_array}, no pipe is invoked and no display updated;
if we **replace** the #{_array}, then the pipe executes and the display is updated*.
The *Flying Heroes* extends the
code with checkbox switches and additional displays to help us experience these effects.
figure.image-display
img(src='/resources/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes")
:marked
Replacing the #{_array} is an efficient way to signal to Angular that it should update the display.
When do we replace the #{_array}? When the data change.
That's an easy rule to follow in *this toy* example
where the only way to change the data is by adding a new hero.
More often we don't know when the data have changed,
especially in applications that mutate data in many ways,
perhaps in application locations far away.
A component in such an application usually can't know about those changes.
Moreover, it's unwise to distort our component design to accommodate a pipe.
We strive as much as possible to keep the component class independent of the HTML.
The component should be unaware of pipes.
Perhaps we should consider a different kind of pipe for filtering flying heroes, an *impure pipe*.
.l-main-section
:marked
## Pure and Impure Pipes
There are two categories of pipes: **pure** and **impure**.
Pipes are pure by default. Every pipe we've seen so far has been pure.
We make a pipe impure by setting its pure flag to false. We could make the `FlyingHeroesPipe`
impure like this:
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pipe-decorator')(format='.')
:marked
Before we do that, let's understand the difference between *pure* and *impure*, starting with a *pure* pipe.
### Pure pipes
block pure-change
:marked
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
A ***pure change*** is *either* a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`)
*or* a changed object reference (`Date`, `Array`, `Function`, `Object`).
:marked
Angular ignores changes *within* (composite) objects.
It won't call a pure pipe if we change an input month, add to an input #{_array}, or update an input object property.
This may seem restrictive but is is also fast.
An object reference check is fast &mdash; much faster than a deep check for
differences &mdash; so Angular can quickly determine if it can skip both the
pipe execution and a view update.
For this reason, we prefer a pure pipe if we can live with the change detection strategy.
When we can't, we *may* turn to the impure pipe.
.l-sub-section
:marked
Or we might not use a pipe at all.
It may be better to pursue the pipe's purpose with a property of the component,
a point we take up later.
:marked
### Impure pipes
Angular executes an *impure pipe* during *every* component change detection cycle.
An impure pipe will be called a lot, as often as every keystroke or mouse-move.
With that concern in mind, we must implement an impure pipe with great care.
An expensive, long-running pipe could destroy the user experience.
<a id="impure-flying-heroes"></a>
### An impure *FlyingHeroesPipe*
A flip of the switch turns our `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
Here's the complete implementation:
+makeTabs(
'pipes/ts/app/flying-heroes.pipe.ts, pipes/ts/app/flying-heroes.pipe.ts',
'impure, pure',
'FlyingHeroesImpurePipe, FlyingHeroesPipe')(format='.')
:marked
We inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
The only difference is the `pure` flag in the pipe metadata.
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
+makeExample('pipes/ts/app/flying-heroes.pipe.ts','filter')(format='.')
We can derive a `FlyingHeroesImpureComponent` that we derive from the `FlyingHeroesComponent`.
+makeExample('pipes/ts/app/flying-heroes.component.ts','impure-component','app/flying-heroes.component.ts (FlyingHeroesImpureComponent)')(format='.')
:marked
The only substantive change is the pipe.
We can confirm in the <live-example></live-example> that the _flying heroes_
display updates as we enter new heroes even when we mutate the `heroes` #{_array}.
- var _dollar = _docsFor === 'ts' ? '$' : '';
h3#async-pipe The impure #[i AsyncPipe]
:marked
The Angular `AsyncPipe` is an interesting example of an impure pipe.
The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input
and subscribes to the input automatically, eventually returning the emitted value(s).
It is also stateful.
The pipe maintains a subscription to the input `#{_Observable}` and
keeps delivering values from that `#{_Observable}` as they arrive.
In this next example, we bind an `#{_Observable}` of message strings
(`message#{_dollar}`) to a view with the `async` pipe.
+makeExample('pipes/ts/app/hero-async-message.component.ts', null, 'app/hero-async-message.component.ts')
:marked
The Async pipe saves boilerplate in the component code.
The component doesn't have to subscribe to the async data source,
it doesn't extract the resolved values and expose them for binding,
and the component doesn't have to unsubscribe when it is destroyed
(a potent source of memory leaks).
### An impure caching pipe
Let's write one more impure pipe, a pipe that makes an HTTP request to the server.
Normally, that's a horrible idea.
It's probably a horrible idea no matter what we do.
We're forging ahead anyway to make a point.
Remember that impure pipes are called every few microseconds.
If we're not careful, this pipe will punish the server with requests.
We are careful. Our pipe only makes a server call if the request URL has changed.
It caches the request URL and waits for a result which it also caches when it arrives.
The pipe returns the cached result (which is null while a request is in flight)
after every Angular call and only contacts the server as necessary.
Here's the code, which uses the [Angular http](server-communication.html) facility
to retrieve a `heroes.json` file:
+makeExample('pipes/ts/app/fetch-json.pipe.ts', null, 'app/fetch-json.pipe.ts')
:marked
Then we demonstrate it in a harness component whose template defines two bindings to this pipe.
+makeExample('pipes/ts/app/hero-list.component.ts', 'template', 'app/hero-list.component.ts (template)')
:marked
Despite the two bindings and what we know to be frequent pipe calls,
the nework tab in the browser developer tools confirms that there is only one request for the file.
The component renders like this:
figure.image-display
img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List")
:marked
### *JsonPipe*
The second binding involving the `FetchPipe` uses more pipe chaining.
We take the same fetched results displayed in the first binding
and display them again, this time in JSON format by chaining through to the built-in `JsonPipe`.
.callout.is-helpful
header Debugging with the json pipe
:marked
The [JsonPipe](../api/common/index/JsonPipe-class.html)
provides an easy way to diagnosis a mysteriously failing data binding or
inspect an object for future binding.
:marked
Here's the complete component implementation:
+makeExample('pipes/ts/app/hero-list.component.ts', null, 'app/hero-list.component.ts')
a(id="pure-pipe-pure-fn")
:marked
### Pure pipes and pure functions
A pure pipe uses pure functions.
Pure functions process inputs and return values without detectable side-effects.
Given the same input they should always return the same output.
The pipes we saw earlier in this chapter were implemented with pure functions.
The built-in `DatePipe` is a pure pipe with a pure function implementation.
So is our `ExponentialStrengthPipe`.
So is our `FlyingHeroesPipe`.
A few steps back we reviewed the `FlyingHeroesImpurePipe` &mdash; *an impure pipe with a pure function*.
But a *pure pipe* must always be implemented with a *pure function*. Failure to heed this warning will bring about many a console errors regarding expressions that have changed after they were checked.
.l-main-section
:marked
## Next Steps
Pipes are a great way to encapsulate and share common display-value
transformations. We use them like styles, dropping them
into our templates expressions to enrich the appeal and usability
of our views.
Explore Angular's inventory of built-in pipes in the [API Reference](../api/#!?apiFilter=pipe).
Try writing a custom pipe and perhaps contributing it to the community.
a(id="no-filter-pipe")
.l-main-section
:marked
## No *FilterPipe* or *OrderByPipe*
Angular does not ship with pipes for filtering or sorting lists.
Developers familiar with Angular 1 know these as `filter` and `orderBy`.
There are no equivalents in Angular 2.
This is not an oversight. Angular 2 is unlikely to offer such pipes because
(a) they perform poorly and (b) they prevent aggressive minification.
Both `filter` and `orderBy` require parameters that reference object properties.
We learned earlier that such pipes must be [*impure*](#pure-and-impure-pipes) and that
Angular calls impure pipes in almost every change detection cycle.
Filtering and especially sorting are expensive operations.
The user experience can degrade severely for even moderate sized lists when Angular calls these pipe methods many times per second.
The `filter` and `orderBy` have often been abused in Angular 1 apps, leading to complaints that Angular itself is slow.
That charge is fair in the indirect sense that Angular 1 prepared this performance trap
by offering `filter` and `orderBy` in the first place.
The minification hazard is also compelling if less obvious. Imagine a sorting pipe applied to a list of heroes.
We might sort the list by hero `name` and `planet` of origin properties something like this:
code-example(language="html")
&lt;!-- NOT REAL CODE! -->
&lt;div *ngFor="let hero of heroes | orderBy:'name,planet'">&lt;/div>
:marked
We identify the sort fields by text strings, expecting the pipe to reference a property value by indexing
(e.g., `hero['name']`).
Unfortunately, aggressive minification *munges* the `Hero` property names so that `Hero.name` and `Hero.planet`
becomes something like `Hero.a` and `Hero.b`. Clearly `hero['name']` is not going to work.
Some of us may not care to minify this aggressively. That's *our* choice.
But the Angular product should not prevent someone else from minifying aggressively.
Therefore, the Angular team decided that everything shipped in Angular will minify safely.
The Angular team and many experienced Angular developers strongly recommend that you move
filtering and sorting logic into the component itself.
The component can expose a `filteredHeroes` or `sortedHeroes` property and take control
over when and how often to execute the supporting logic.
Any capabilities that you would have put in a pipe and shared across the app can be
written in a filtering/sorting service and injected into the component.
If these performance and minification considerations do not apply to you, you can always create your own such pipes
(along the lines of the [FlyingHeroesPipe](#impure-flying-heroes)) or find them in the community.

View File

@ -0,0 +1,263 @@
block includes
include ../_util-fns
:marked
Web application security has many aspects. This chapter describes Angular's built in
protections against common web application vulnerabilities and attacks, such as Cross Site
Scripting Attacks. It does not cover application level security, such as authentication (_Who is
this user?_) or authorization (_What can this user do?_).
The [Open Web Application Security Project (OWASP)](https://www.owasp.org/index.php/Category:OWASP_Guide_Project)
has further information on the attacks and mitigations described below.
.l-main-section
:marked
# Table Of Contents
* [Reporting Vulnerabilities](#report-issues)
* [Best Practices](#best-practices)
* [Preventing Cross-Site Scripting (XSS)](#xss)
* [Trusting Safe Values](#bypass-security-apis)
* [HTTP-level Vulnerabilities](#http)
* [Auditing Angular Applications](#code-review)
Try the <live-example></live-example> of the code shown in this chapter.
.l-main-section
h2#report-issues Reporting Vulnerabilities
:marked
Email us at [security@angular.io](mailto:security@angular.io) to report vulnerabilities in
Angular itself.
For further details on how Google handles security issues please refer to [Google's security
philosophy](https://www.google.com/about/appsecurity/).
.l-main-section
h2#best-practices Best Practices
:marked
* **Keep current with the latest Angular library releases.**
We regularly update our Angular libraries and these updates may fix security defects discovered in
previous version. Check the Angular [change
log](https://github.com/angular/angular/blob/master/CHANGELOG.md) for security-related updates.
* **Don't modify your copy of Angular.**
Private, customized versions of Angular tend to fall behind the current version and may neglect
important security fixes and enhancements. Instead, share your Angular improvements with the
community and make a pull request.
* **Avoid Angular APIs marked in the documentation as “[_Security Risk_](#bypass-security-apis)”.**
.l-main-section
h2#xss Preventing Cross-Site Scripting (XSS)
:marked
[Cross-Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers
to inject malicious code into web pages. Such code can then, for example, steal user's data (in
particular their login data), or perform actions impersonating the user. This is one of the most
common attacks on the web.
To block XSS attacks, we must prevent malicious code from entering the DOM. For example, if an
attacker can trick us into inserting a `<script>` tag in the DOM, they can run arbitrary code on
our website. The attack is not limited to `<script>` tags - many elements and properties in the
DOM allow code execution, for example `<img onerror="...">`, `<a href="javascript:...">`. If
attacker controlled data enters the DOM, we have to expect security vulnerabilities.
### Angulars Cross-site Scripting Security Model
To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value
is inserted into the DOM from a template, via property, attribute, style, or class binding, or via
interpolation, Angular will sanitize and escape untrusted values.
**Angular templates are the same as executable code**: HTML, attributes, and binding expressions
(but not the values bound!) in templates are trusted to be safe. That means applications must
prevent potentially attacker controlled values from ever making it into the source code of a
template. Never generate template source code by concatenating user input and templates! Using
the [offline template compiler](#offline-template-compiler) is an effective way to prevent these
vulnerabilities, also known as template injection.
### Sanitization and security contexts
Sanitization inspects an untrusted value and turns it into a value that is safe to insert into
the DOM. In many cases, values do not get changed by this at all. Sanitization depends on context:
a value that is harmless in CSS is potentially dangerous in a URL.
Angular defines four security contexts: HTML, style, URL, and resource URL.
* HTML is used when interpreting a value as HTML, e.g., when binding to `innerHtml`
* Style is used when binding CSS into the `style` property
* URL is used for URL properties such as `<a href>`
* Resource URLs are URLs that will be loaded and executed as code, e.g., in `<script src>`
Angular sanitizes untrusted values for the first three items; sanitizing resource URLs is not
possible as they contain arbitrary code. In development mode, Angular prints a console warning
when it has to change a value during sanitization.
### Sanitization example
The template below binds the value of `htmlSnippet`, once by interpolating it into an element's
content, and once by binding it to the `innerHTML` property of an element.
+makeExample('app/inner-html-binding.component.html')
:marked
Interpolated content is always escaped - the HTML is not interpreted, and the browser displays
angle brackets in the elements text content.
For the HTML to be interpreted, we must bind to an HTML property, such as `innerHTML`. But binding
a potentially attacker controlled value into `innerHTML` would normally cause an XSS
vulnerability. For example, code contained in a `<script>` tag would be executed.
+makeExcerpt('app/inner-html-binding.component.ts ()', 'inner-html-controller')
:marked
Angular recognizes the value as unsafe, and automatically sanitizes it. It removes the `<script>`
tag but keeps safe content, such as the text content of the `<script>` tag, or the `<b>` element.
figure.image-display
img(src='/resources/images/devguide/security/binding-inner-html.png'
alt='A screenshot showing interpolated and bound HTML values')
:marked
### Avoid direct use of the DOM APIs
The built-in browser DOM APIs do not automatically protect you from security vulnerabilities.
For example, `document`, the node available through `ElementRef`, and many third party APIs
contain unsafe methods. Avoid directly interacting with the DOM, and instead use Angular
templates where possible.
### Content Security Policy
A [Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
`Content-Security-Policy` HTTP header.
<a id="offline-template-compiler"></a>
### Use the Offline Template Compiler
The offline template compiler prevents a whole class of vulnerabilities called template injection,
and also greatly improves application performance. Use the offline template compiler in production
deployments. Do not dynamically generate templates. Angular trusts template code, so generating
templates, in particular containing user data, circumvents Angular's built-in protections. See the
[Dynamic Forms Cookbook](../cookbook/dynamic-form.html) on how to dynamically construct forms in a
safe way.
### Server side XSS protection
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
Angular application is the same as injecting executable code into the
application; it gives the attacker full control over the application. To prevent this, make sure
to use a templating language that automatically escapes values to prevent XSS vulnerabilities on
the server. Do not generate Angular templates on the server side using a templating language, this
carries a high risk of introducing template injection vulnerabilities.
.l-main-section
h2#bypass-security-apis Trusting Safe Values
:marked
Sometimes applications genuinely need to include executable code, display an `<iframe>` from some
URL, or construct potentially dangerous URLs. To prevent automatic sanitization in this situation,
you can tell Angular that you inspected a value, checked how it is generated, and made sure it is
always secure. But **be careful**! If you trust a value that can be malicious, you will likely
introduce a security vulnerability into your application. If in doubt, find a professional
security reviewer.
You can mark a value as trusted by injecting `DomSanitizationService`, and calling one of the
following methods.
* `bypassSecurityTrustHtml`
* `bypassSecurityTrustScript`
* `bypassSecurityTrustStyle`
* `bypassSecurityTrustUrl`
* `bypassSecurityTrustResourceUrl`
Remember, whether a value is safe depends on context, so you need to choose the right context for
your intended use of the value. Imagine the following template needs to bind a URL to a
`javascript:alert(...)` call.
+makeExcerpt('app/bypass-security.component.html ()', 'dangerous-url')
:marked
Normally, Angular automatically sanitizes the URL, disables the dangerous code and,
in development mode, logs this action to the console. To prevent
this, we can mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
+makeExcerpt('app/bypass-security.component.ts ()', 'trust-url')
figure.image-display
img(src='/resources/images/devguide/security/bypass-security-component.png'
alt='A screenshot showing an alert box created from a trusted URL')
:marked
If we need to convert user input into a trusted value, it can be convenient to do so in a
controller method. The template below allows users to enter a YouTube video ID, and load the
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
context, because an untrusted source can, e.g., smuggle in file downloads that unsuspecting users
would execute. So we call a method on the controller to construct a trusted video URL, which
Angular then allows binding into `<iframe src>`.
+makeExcerpt('app/bypass-security.component.html ()', 'iframe-videoid')
+makeExcerpt('app/bypass-security.component.ts ()', 'trust-video-url')
.l-main-section
h2#http HTTP-level Vulnerabilities
:marked
Angular has built in support to help prevent two common HTTP vulnerabilities, Cross-site Request
Forgery (XSRF) and Cross-site Script Inclusion (XSSI). Both of these must be primarily mitigated
on the server side, but Angular ships helpers to make integration on the client side easier.
h3#xsrf Cross-site Request Forgery (XSRF)
:marked
In a Cross-site Request Forgery (XSRF or CSRF), an attacker tricks the user into visiting a
_different_ page, and has them, e.g., submit a form that sends a request to your application's
web server. If the user is logged into your application, the browser will send authentication
cookies, and the attacker could &mdash; for example &mdash; cause a bank transfer in the user's name with
the right request.
To prevent this, your application must ensure that user requests originate in your own
application, not on a different site. A common technique is that the server sends a randomly
generated authentication token in a cookie, often with the name `XSRF-TOKEN`. Cookies can only
be read by the website on which they are set, so only your own application can read this token. On
each API request, the server then validates the client by checking that the token is sent back,
usually in an HTTP header called `X-XSRF-TOKEN`.
The Angular `http` client has built-in support for this technique. The default
`CookieXSRFStrategy` looks for a cookie called `XSRF-TOKEN` and sets an HTTP request header named
`X-XSRF-TOKEN` with the value of that cookie on every request. The server must set the
`XSRF-TOKEN` cookie, and validate the response header for each state modifying request.
XSRF tokens should be unique per user and session, have a large random value generated by a
cryptographically secure random number generator, and expire.
Angular applications can customize cookie and header names by binding their own
`CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom
binding for that type, by adding either of the following to your providers list:
code-example(language="typescript").
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')}
{ provide: XSRFStrategy, useClass: MyXSRFStrategy}
:marked
Learn about Cross Site Request Forgery (XSRF) at the Open Web Application Security Project (OWASP)
[here](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) and
[here](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet). This [Stanford University
paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is also a rich source of detail.
h3#xssi Cross-site Script Inclusion (XSSI)
:marked
Cross-site Script Inclusion, also known as JSON vulnerability, can allow an attacker's website to
read data from a JSON API. The attack works on older browser by overriding native JavaScript
object constructors, and then including an API URL using a `<script>` tag.
This attack is only successful if the returned JSON is executable as JavaScript. Servers can
prevent it by prefixing all JSON responses to make them non-executable, by convention using the
well-known string `")]}',\n"`.
Angular's `Http` library recognizes this convention and automatically strips the string
`")]}',\n"` from all responses before further parsing.
Learn more in the XSSI section of this [Google web security blog
post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html)
.l-main-section
h2#code-review Auditing Angular Applications
:marked
Angular applications should follow the same security principles as regular web applications, and
should be audited as such. Angular specific APIs that should be audited in a security review,
such as the [_bypassSecurityTrust_](#bypass-security-apis) APIs, are marked in the documentation
as security sensitive.

View File

@ -0,0 +1,702 @@
block includes
include ../_util-fns
- var _Http = 'Http'; // Angular `Http` library name.
- var _Angular_Http = 'Angular <code>Http</code>'
- var _Angular_http_library = 'Angular HTTP library'
:marked
[HTTP](https://tools.ietf.org/html/rfc2616) is the primary protocol for browser/server communication.
.l-sub-section
:marked
The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology;
we won't cover it in this chapter.
:marked
Modern browsers support two HTTP-based APIs:
[XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
The !{_Angular_http_library} simplifies application programming of the **XHR** and **JSONP** APIs
as we'll learn in this chapter covering:
- [HTTP client sample overview](#http-client)
- [Fetch data with http.get](#fetch-data)
<li if-docs="ts"> [RxJS Observable of HTTP Responses](#rxjs)</li>
<li if-docs="ts"> [Enabling RxJS Operators](#enable-rxjs-operators)</li>
- [Extract JSON data](#extract-data)
- [Error handling](#error-handling)
- [Send data to the server](#update)
<li if-docs="ts"> [Promises instead of observables](#promises)</li>
- [Cross-origin requests: Wikipedia example](#cors)
<ul if-docs="ts">
<li> [Set query string parameters](#search-parameters)</li>
<li> [Debounce search term input](#more-observables)</li>
</ul>
- [Appendix: the in-memory web api service](#in-mem-web-api)
We illustrate these topics with code that you can <live-example>run live</live-example>.
.l-main-section
:marked
# Demos
This chapter describes server communication with the help of the following demos
block demos-list
:marked
- [HTTP client: Tour of Heroes with Observables](#http-client)
- [HTTP client: Tour of Heroes with !{_Promise}s](#promises)
- [JSONP client: Wikipedia to fetch data from a service that does not support CORS](#cors)
- [JSONP client: Wikipedia using observable operators to reduce server calls](#more-observables)
:marked
These demos are orchestrated by the root `AppComponent`
+makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts')
+ifDocsFor('ts')
:marked
There is nothing remarkable here _except_ for the import of RxJS operators.
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.')
:marked
We'll talk about that [below](#rxjs) when we're ready to explore observables.
:marked
First, we have to configure our application to use server communication facilities.
.l-main-section#http-providers
:marked
# Providing HTTP Services
We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol.
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
+ifDocsFor('ts')
.l-sub-section
:marked
SystemJS knows how to load services from the !{_Angular_http_library} when we import from the `@angular/http` module
because we registered that module name in the `system.config` file.
:marked
Before we can use the `!{_Http}` client , we'll have to register it as a service provider with the Dependency Injection system.
.l-sub-section
:marked
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
:marked
In this demo, we register providers in the `bootstrap()` method of
<span ngio-ex>app/main.ts</span>.
+makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.')
block http-providers
:marked
We begin by importing the symbols we need, most of them familiar by now. The newcomer is `HTTP_PROVIDERS`,
a collection of service providers from the !{_Angular_http_library}.
We register HTTP providers in the bootstrap method by passing them in an array as the second parameter after the root component.
### Why register in *bootstrap*?
We prefer to register application-wide providers in the metadata `providers` array
of the root `AppComponent` like this:
+makeExample('server-communication/ts/app/app.component.ts','http-providers')(format='.')
:marked
Here we register the providers in the `bootstrap` method in the `main.ts` file. Why?
This is a *sample application* that doesn't talk to a real server.
We're going to reconfigure the (typically-hidden) `XhrBackend` service with a fake provider
that fetches and saves sample data from an in-memory data store.
This replacement service is called the [*in-memory web api*](#in-mem-web-api).
Such sleight-of-hand is something the root application component should *not* know about.
For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`.
.l-main-section#http-client
:marked
# The Tour of Heroes _HTTP_ Client Demo
Our first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
This version gets some heroes from the server, displays them in a list, lets us add new heroes, and saves them to the server.
We use the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
It works like this.
figure.image-display
img(src='/resources/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250")
:marked
This demo has a single component, the `HeroListComponent`. Here's its template:
+makeExample('server-communication/ts/app/toh/hero-list.component.html', null, 'app/toh/hero-list.component.html (template)')
:marked
It presents the list of heroes with an `ngFor`.
Below the list is an input box and an *Add Hero* button where we can enter the names of new heroes
and add them to the database.
We use a [template reference variable](template-syntax.html#ref-vars), `newHeroName`, to access the
value of the input box in the `(click)` event binding.
When the user clicks the button, we pass that value to the component's `addHero` method and then
clear it to make it ready for a new hero name.
Below the button is an area for an error message.
a#oninit
a#HeroListComponent
:marked
## The *HeroListComponent* class
Here's the component class:
+makeExample('server-communication/ts/app/toh/hero-list.component.ts','component', 'app/toh/hero-list.component.ts (class)')
:marked
Angular [injects](dependency-injection.html) a `HeroService` into the constructor
and the component calls that service to fetch and save data.
The component **does not talk directly to the !{_Angular_Http} client**!
The component doesn't know or care how we get the data.
It delegates to the `HeroService`.
This is a golden rule: **always delegate data access to a supporting service class**.
Although _at runtime_ the component requests heroes immediately after creation,
we do **not** call the service's `get` method in the component's constructor.
We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead
and count on Angular to call `ngOnInit` when it instantiates this component.
.l-sub-section
:marked
This is a *best practice*.
Components are easier to test and debug when their constructors are simple and all real work
(especially calling a remote server) is handled in a separate method.
block getheroes-and-addhero
:marked
The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server.
*Observables* are a big topic, beyond the scope of this chapter.
But we need to know a little about them to appreciate what is going on here.
We should think of an `Observable` as a stream of events published by some source.
We listen for events in this stream by ***subscribing*** to the `Observable`.
In these subscriptions we specify the actions to take when the web request
produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload).
:marked
With our basic intuitions about the component squared away, we're ready to look inside the `HeroService`.
a#HeroService
.l-main-section#fetch-data
:marked
## Fetch data with the **HeroService**
In many of our previous samples we faked the interaction with the server by
returning mock heroes in a service like this one:
+makeExample('toh-4/ts/app/hero.service.ts', 'just-get-heroes')(format=".")
:marked
In this chapter, we revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts (revised)')
:marked
Notice that the !{_Angular_Http} client service is
[injected](dependency-injection.html) into the `HeroService` constructor.
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'ctor')
:marked
Look closely at how we call `!{_priv}http.get`
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get', 'app/toh/hero.service.ts (getHeroes)')(format=".")
:marked
We pass the resource URL to `get` and it calls the server which should return heroes.
.l-sub-section
:marked
It *will* return heroes once we've set up the [in-memory web api](#in-mem-web-api)
described in the appendix below.
Alternatively, we can (temporarily) target a JSON file by changing the endpoint URL:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
+ifDocsFor('ts')
:marked
<a id="rxjs"></a>
The return value may surprise us.
Many of us who are familiar with asynchronous methods in modern JavaScript would expect the `get` method to return a
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
We'd expect to chain a call to `then()` and extract the heroes.
Instead we're calling a `map()` method.
Clearly this is not a promise.
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
and `map` is one of the RxJS *operators*.
.l-main-section
:marked
# RxJS Library
[RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions") is a 3rd party library, endorsed by Angular,
that implements the [*asynchronous observable*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") pattern.
All of our Developer Guide samples have installed the RxJS npm package and loaded via `system.js`
because observables are used widely in Angular applications.
We certainly need it now when working with the HTTP client.
And we must take a critical extra step to make RxJS observables usable.
### Enable RxJS Operators
The RxJS library is quite large.
Size matters when we build a production application and deploy it to mobile devices.
We should include only those features that we actually need.
Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module,
a version that lacks most of the operators including some we'd like to use here
such as the `map` method we called above in `getHeroes`.
It's up to us to add the operators we need.
We could add _every_ RxJS operators with a single import statement.
While that is the easiest thing to do, we'd pay a penalty in extended launch time and application size
because the full library is so big. We only use a few operators in our app.
Instead, we'll import each `Observable` operator and static class method, one-by-one, until we have a custom *Observable* implementation tuned
precisely to our requirements. We'll put the `import` statements in one `app/rxjs-operators.ts` file.
+makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".")
:marked
If we forget an operator, the TypeScript compiler will warn that it's missing and we'll update this file.
.l-sub-section
:marked
We don't need _all_ of these particular operators in the `HeroService` &mdash; just `map`, `catch` and `throw`.
We'll need the other operators later, in a *Wiki* example [below](#more-observables).
:marked
Finally, we import `rxjs-operator`_itself_ in our `app.component.ts`:
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs', 'app/app.component.ts (import rxjs)')(format=".")
:marked
Let's return to our study of the `HeroService`.
l-main-section
a#extract-data
:marked
## Process the response object
Remember that our `getHeroes()` method mapped the `!{_priv}http.get` response object to heroes with an `!{_priv}extractData` helper method:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (excerpt)')(format=".")
:marked
The `response` object does not hold our data in a form we can use directly.
To make it useful in our application we must parse the response data into a JSON object
#### Parse to JSON
block parse-json
:marked
The response data are in JSON string form.
We must parse that string into JavaScript objects which we do by calling `response.json()`.
.l-sub-section
:marked
This is not Angular's own design.
The Angular HTTP client follows the ES2015 specification for the
[response object](https://fetch.spec.whatwg.org/#response-class) returned by the `Fetch` function.
That spec defines a `json()` method that parses the response body into a JavaScript object.
.l-sub-section
:marked
We shouldn't expect the decoded JSON to be the heroes !{_array} directly.
The server we're calling always wraps JSON results in an object with a `data`
property. We have to unwrap it to get the heroes.
This is conventional web api behavior, driven by
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
.alert.is-important
:marked
Make no assumptions about the server API.
Not all servers return an object with a `data` property.
:marked
### Do not return the response object
Our `getHeroes()` could have returned the HTTP response. Bad idea!
The point of a data service is to hide the server interaction details from consumers.
The component that calls the `HeroService` wants heroes.
It has no interest in what we do to get them.
It doesn't care where they come from.
And it certainly doesn't want to deal with a response object.
+ifDocsFor('ts')
.callout.is-important
header HTTP GET is delayed
:marked
The `!{_priv}http.get` does **not send the request just yet!** This observable is
[*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables)
which means the request won't go out until something *subscribes* to the observable.
That *something* is the [HeroListComponent](#subscribe).
a#error-handling
:marked
### Always handle errors
Whenever we deal with I/O we must be prepared for something to go wrong as it surely will.
We should catch errors in the `HeroService` and do something with them.
We may also pass an error message back to the component for presentation to the user
but only if we can say something the user can understand and act upon.
In this simple app we provide rudimentary error handling in both the service and the component.
block error-handling
:marked
The eagle-eyed reader may have spotted our use of the `catch` operator in conjunction with a `handleError` method.
We haven't discussed so far how that actually works.
We use the Observable `catch` operator on the service level.
It takes an error handling function with an error object as the argument.
Our service handler, `handleError`, logs the response to the console,
transforms the error into a user-friendly message, and returns the message in a new, failed observable via `Observable.throw`.
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'error-handling', 'app/toh/hero.service.ts (excerpt)')(format=".")
a#subscribe
a#hero-list-component
h4 #[b HeroListComponent] error handling
block hlc-error-handling
:marked
Back in the `HeroListComponent`, where we called `!{_priv}heroService.getHeroes()`,
we supply the `subscribe` function with a second function parameter to handle the error message.
It sets an `errorMessage` variable which we've bound conditionally in the `HeroListComponent` template.
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'getHeroes', 'app/toh/hero-list.component.ts (getHeroes)')(format=".")
.l-sub-section
:marked
Want to see it fail? Reset the api endpoint in the `HeroService` to a bad value. Remember to restore it!
<a id="update"></a>
<a id="post"></a>
.l-main-section
:marked
## Send data to the server
So far we've seen how to retrieve data from a remote location using an HTTP service.
Let's add the ability to create new heroes and save them in the backend.
We'll create an easy method for the `HeroListComponent` to call, an `addHero()` method that takes
just the name of a new hero:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'addhero-sig')(format=".")
:marked
To implement it, we need to know some details about the server's api for creating heroes.
[Our data server](#server) follows typical REST guidelines.
It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request
at the same endpoint where we `GET` heroes.
It expects the new hero data to arrive in the body of the request,
structured like a `Hero` entity but without the `id` property.
The body of the request should look like this:
code-example(format="." language="javascript").
{ "name": "Windstorm" }
:marked
The server will generate the `id` and return the entire `JSON` representation
of the new hero including its generated id. The hero arrives tucked inside a response object
with its own `data` property.
Now that we know how the API works, we implement `addHero()`like this:
+ifDocsFor('ts')
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'import-request-options', 'app/toh/hero.service.ts (additional imports)')(format=".")
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'addhero', 'app/toh/hero.service.ts (addHero)')(format=".")
:marked
### Headers
The `Content-Type` header allows us to inform the server that the body will represent JSON.
+ifDocsFor('ts')
:marked
[Headers](../api/http/index/Headers-class.html) are one of the [RequestOptions](../api/http/index/RequestOptions-class.html).
Compose the options object and pass it in as the *third* parameter of the `post` method, as shown above.
:marked
### Body
Despite the content type being specified as JSON, the POST body must actually be a *string*.
Hence, we explicitly encode the JSON hero content before passing it in as the body argument.
+ifDocsFor('ts')
.l-sub-section
:marked
We may be able to skip the `JSON.stringify` step in the near future.
:marked
### JSON results
As with `getHeroes()`, we [extract the data](#extract-data) from the response using the
`!{_priv}extractData()` helper.
block hero-list-comp-add-hero
:marked
Back in the `HeroListComponent`, we see that *its* `addHero()` method subscribes to the observable returned by the *service's* `addHero()` method.
When the data, arrive it pushes the new hero object into its `heroes` array for presentation to the user.
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
+ifDocsFor('ts')
h2#promises Fall back to Promises
:marked
Although the Angular `http` client API returns an `Observable<Response>` we can turn it into a
[Promise<Response>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) if we prefer.
It's easy to do and a promise-based version looks much like the observable-based version in simple cases.
.l-sub-section
:marked
While promises may be more familiar, observables have many advantages.
Don't rush to promises until you give observables a chance.
:marked
Let's rewrite the `HeroService` using promises , highlighting just the parts that are different.
+makeTabs(
'server-communication/ts/app/toh/hero.service.promise.ts,server-communication/ts/app/toh/hero.service.ts',
'methods, methods',
'app/toh/hero.service.promise.ts (promise-based), app/toh/hero.service.ts (observable-based)')
:marked
Converting from an observable to a promise is as simple as calling `toPromise(success, fail)`.
We move the observable's `map` callback to the first *success* parameter and its `catch` callback to the second *fail* parameter
and we're done!
Or we can follow the promise `then.catch` pattern as we do in the second `addHero` example.
Our `errorHandler` forwards an error message as a failed promise instead of a failed Observable.
The diagnostic *log to console* is just one more `then` in the promise chain.
We have to adjust the calling component to expect a `Promise` instead of an `Observable`.
+makeTabs(
'server-communication/ts/app/toh/hero-list.component.promise.ts, server-communication/ts/app/toh/hero-list.component.ts',
'methods, methods',
'app/toh/hero-list.component.promise.ts (promise-based), app/toh/hero-list.component.ts (observable-based)')
:marked
The only obvious difference is that we call `then` on the returned promise instead of `subscribe`.
We give both methods the same functional arguments.
.l-sub-section
:marked
The less obvious but critical difference is that these two methods return very different results!
The promise-based `then` returns another promise. We 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. We can't call `map` on it or call `subscribe` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
Learn more about observables to understand the implications and consequences of subscriptions.
h2#cors Cross-origin requests: Wikipedia example
:marked
We just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service.
This is the most common approach for server communication.
It doesn't work in all scenarios.
For security reasons, web browsers block `XHR` calls to a remote server whose origin is different from the origin of the web page.
The *origin* is the combination of URI scheme, hostname and port number.
This is called the [Same-origin Policy](https://en.wikipedia.org/wiki/Same-origin_policy).
.l-sub-section
:marked
Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol.
If the server requires user credentials, we'll enable them in the [request headers](#headers).
:marked
Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP).
Wikipedia is one such server.
.l-sub-section
:marked
This [StackOverflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.
:marked
### Search wikipedia
Let's build a simple search that shows suggestions from wikipedia as we type in a text box.
figure.image-display
img(src='/resources/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250")
block wikipedia-jsonp+
:marked
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. Let's use the latter for this example.
The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts us to `GET` requests.
All other HTTP methods throw an error because JSONP is a read-only facility.
As always, we wrap our interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
+makeExample('server-communication/ts/app/wiki/wikipedia.service.ts',null,'app/wiki/wikipedia.service.ts')
:marked
The constructor expects Angular to inject its `jsonp` service.
We register that service with `JSONP_PROVIDERS` in the [component below](#wikicomponent) that calls our `WikipediaService`.
<a id="query-parameters"></a>
:marked
### Search parameters
The [Wikipedia 'opensearch' API](https://www.mediawiki.org/wiki/API:Opensearch)
expects four parameters (key/value pairs) to arrive in the request URL's query string.
The keys are `search`, `action`, `format`, and `callback`.
The value of the `search` key is the user-supplied search term to find in Wikipedia.
The other three are the fixed values "opensearch", "json", and "JSONP_CALLBACK" respectively.
.l-sub-section
:marked
The `JSONP` technique requires that we pass a callback function name to the server in the query string: `callback=JSONP_CALLBACK`.
The server uses that name to build a JavaScript wrapper function in its response which Angular ultimately calls to extract the data.
All of this happens under the hood.
:marked
If we're looking for articles with the word "Angular", we could construct the query string by hand and call `jsonp` like this:
+makeExample('server-communication/ts/app/wiki/wikipedia.service.1.ts','query-string')(format='.')
:marked
In more parameterized examples we might prefer to build the query string with the Angular `URLSearchParams` helper as shown here:
+makeExample('server-communication/ts/app/wiki/wikipedia.service.ts','search-parameters','app/wiki/wikipedia.service.ts (search parameters)')(format=".")
:marked
This time we call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
+makeExample('server-communication/ts/app/wiki/wikipedia.service.ts','call-jsonp','app/wiki/wikipedia.service.ts (call jsonp)')(format=".")
:marked
`Jsonp` flattens the `params` object into the same query string we saw earlier before putting the request on the wire.
<a id="wikicomponent"></a>
:marked
### The WikiComponent
Now that we have a service that can query the Wikipedia API,
we turn to the component that takes user input and displays search results.
+makeExample('server-communication/ts/app/wiki/wiki.component.ts', null, 'app/wiki/wiki.component.ts')
:marked
The `providers` array in the component metadata specifies the Angular `JSONP_PROVIDERS` collection that supports the `Jsonp` service.
We register that collection at the component level to make `Jsonp` injectable in the `WikipediaService`.
The component presents an `<input>` element *search box* to gather search terms from the user.
and calls a `search(term)` method after each `keyup` event.
The `search(term)` method delegates to our `WikipediaService` which returns an observable array of string results (`Observable<string[]>`).
Instead of subscribing to the observable inside the component as we did in the `HeroListComponent`,
we forward the observable result to the template (via `items`) where the [async pipe](pipes.html#async-pipe)
in the `ngFor` handles the subscription.
.l-sub-section
:marked
We often use the [async pipe](pipes.html#async-pipe) in read-only components where the component has no need to interact with the data.
We couldn't use the pipe in the `HeroListComponent` because the "add hero" feature pushes newly created heroes into the list.
:marked
## Our wasteful app
Our wikipedia search makes too many calls to the server.
It is inefficient and potentially expensive on mobile devices with limited data plans.
### 1. Wait for the user to stop typing
At the moment we call the server after every key stroke.
The app should only make requests when the user *stops typing* .
Here's how it *should* work &mdash; and *will* work &mdash; when we're done refactoring:
figure.image-display
img(src='/resources/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250")
:marked
### 2. Search when the search term changes
Suppose the user enters the word *angular* in the search box and pauses for a while.
The application issues a search request for *Angular*.
Then the user backspaces over the last three letters, *lar*, and immediately re-types *lar* before pausing once more.
The search term is still "angular". The app shouldn't make another request.
### 3. Cope with out-of-order responses
The user enters *angular*, pauses, clears the search box, and enters *http*.
The application issues two search requests, one for *angular* and one for *http*.
Which response will arrive first? We can't be sure.
A load balancer could dispatch the requests to two different servers with different response times.
The results from the first *angular* request might arrive after the later *http* results.
The user will be confused if we display the *angular* results to the *http* query.
When there are multiple requests in-flight, the app should present the responses
in the original request order. That won't happen if *angular* results arrive last.
<a id="more-observables"></a>
## More fun with Observables
We can address these problems and improve our app with the help of some nifty observable operators.
We could make our changes to the `WikipediaService`.
But we sense that our concerns are driven by the user experience so we update the component class instead.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', null, 'app/wiki/wiki-smart.component.ts')
:marked
We made no changes to the template or metadata, confining them all to the component class.
Let's review those changes.
### Create a stream of search terms
We're binding to the search box `keyup` event and calling the component's `search` method after each keystroke.
We turn these events into an observable stream of search terms using a `Subject`
which we import from the RxJS observable library:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject')
:marked
Each search term is a string, so we create a new `Subject` of type `string` called `searchTermStream`.
After every keystroke, the `search` method adds the search box value to that stream
via the subject's `next` method.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
:marked
### Listen for search terms
Earlier, we passed each search term directly to the service and bound the template to the service results.
Now we listen to the *stream of terms*, manipulating the stream before it reaches the `WikipediaService`.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
:marked
We wait for the user to stop typing for at least 300 milliseconds
([debounceTime](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md)).
Only changed search values make it through to the service
([distinctUntilChanged](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)).
The `WikipediaService` returns a separate observable of string arrays (`Observable<string[]>`) for each request.
We could have multiple requests *in flight*, all awaiting the server's reply,
which means multiple *observables-of-strings* could arrive at any moment in any order.
The [switchMap](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md)
(formerly known as `flatMapLatest`) returns a new observable that combines these `WikipediaService` observables,
re-arranges them in their original request order,
and delivers to subscribers only the most recent search results.
The displayed list of search results stays in sync with the user's sequence of search terms.
.l-sub-section
:marked
We added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class
in `rxjs-operators` as [described above](#rxjs)
a#in-mem-web-api
.l-main-section
:marked
## Appendix: Tour of Heroes in-memory server
If we only cared to retrieve data, we could tell Angular to get the heroes from a `heroes.json` file like this one:
+makeJson('server-communication/ts/app/heroes.json', null, 'app/heroes.json')(format=".")
.l-sub-section
:marked
We wrap the heroes array in an object with a `data` property for the same reason that a data server does:
to mitigate the [security risk](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk)
posed by top-level JSON arrays.
:marked
We'd set the endpoint to the JSON file like this:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
- var _a_ca_class_with = _docsFor === 'ts' ? 'a custom application class with' : ''
:marked
The *get heroes* scenario would work.
But we want to *save* data too. We can't save changes to a JSON file. We need a web API server.
We didn't want the hassle of setting up and maintaining a real server for this chapter.
So we turned to an *in-memory web API simulator* instead.
.l-sub-section
:marked
The in-memory web api is not part of the Angular core.
It's an optional service in its own `angular2-in-memory-web-api` library
that we installed with npm (see `package.json`) and
registered for module loading by SystemJS (see `systemjs.config.js`)
:marked
The in-memory web API gets its data from !{_a_ca_class_with} a `createDb()`
method that returns a map whose keys are collection names and whose values
are !{_array}s of objects in those collections.
Here's the class we created for this sample based on the JSON data:
+makeExample('server-communication/ts/app/hero-data.ts', null, 'app/hero-data.ts')(format=".")
:marked
Ensure that the `HeroService` endpoint refers to the web API:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint')(format=".")
:marked
Finally, we need to redirect client HTTP requests to the in-memory web API.
block redirect-to-web-api
:marked
This redirection is easy to configure because Angular's `http` service delegates the client/server communication tasks
to a helper service called the `XHRBackend`.
To enable our server simulation, we replace the default `XHRBackend` service with
the in-memory web API service using standard Angular provider registration techniques.
We initialize the in-memory web API with *seed data* from the mock hero dataset at the same time.
:marked
Here is the revised (and final) version of <span ngio-ex>app/main.ts></span> demonstrating these steps.
+makeExcerpt('app/main.ts', 'final')
:marked
See the full source code in the <live-example></live-example>.

View File

@ -0,0 +1,336 @@
block includes
include ../_util-fns
:marked
One of the defining features of a single page application is its manipulation
of the DOM tree. Instead of serving a whole new page every time a user
navigates, whole sections of the DOM appear and disappear according
to the application state. In this chapter we'll to look at how Angular
manipulates the DOM and how we can do it ourselves in our own directives.
In this chapter we will
- [learn what structural directives are](#definition)
- [study *ngIf*](#ngIf)
- [discover the &lt;template> element](#template)
- [understand the asterisk (\*) in **ngFor*](#asterisk)
- [write our own structural directive](#unless)
Try the <live-example></live-example>.
<a id="definition"></a>
.l-main-section
:marked
## What are structural directives?
There are three kinds of Angular directives:
1. Components
1. Attribute directives
1. Structural directives
The *Component* is really a directive with a template.
It's the most common of the three directives and we write lots of them as we build our application.
The [*Attribute* directive](attribute-directives.html) changes the appearance or behavior of an element.
The built-in [NgStyle](template-syntax.html#ngStyle) directive, for example,
can change several element styles at the same time.
We can use it to render text bold, italic, and lime green by binding to a
component property that requests such a sickening result.
A *Structural* directive changes the DOM layout by adding and removing DOM elements.
We've seen three of the built-in structural directives in other chapters: [ngIf](template-syntax.html#ngIf),
[ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor).
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'structural-directives')(format=".")
<a id="ngIf"></a>
.l-main-section
:marked
## NgIf Case Study
Lets focus on `ngIf`. It's a great example of a structural
directive: it takes a boolean and makes an entire chunk of DOM appear
or disappear.
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf')(format=".")
:marked
The `ngIf` directive does not hide the element.
Using browser developer tools we can see that, when the condition is true, the top
paragraph is in the DOM and the bottom disused paragraph is completely
absent from the DOM! In its place are empty `<script>` tags.
figure.image-display
img(src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="element not in dom")
:marked
### Why *remove* rather than *hide*?
We could hide the unwanted paragraph by setting its css `display` style to `none`.
The element would remain in the DOM while invisible. Instead we removed it with `ngIf`.
The difference matters. When we hide an element,
the component's behavior continues.
It remains attached to its DOM element. It continues to listen to events.
Angular keeps checking for changes that could affect data bindings.
Whatever the component was doing it keeps doing.
Although invisible, the component &mdash; and all of its descendent components &mdash;
tie up resources that might be more useful elsewhere.
The performance and memory burden can be substantial and the user may not benefit at all.
On the positive side, showing the element again is very quick.
The component's previous state is preserved and ready to display.
The component doesn't re-initialize &mdash; an operation that could be expensive.
`ngIf` is different.
Setting `ngIf` to false **does** affect the component's resource consumption.
Angular removes the element from DOM, stops change detection for the associated component,
detaches it from DOM events (the attachments that it made) and destroys the component.
The component can be garbage-collected (we hope) and free up memory.
Components often have child components which themselves have children.
All of them are destroyed when `ngIf` destroys the common ancestor.
This cleanup effort is usually a good thing.
Of course it isn't *always* a good thing.
It might be a bad thing if we need that particular component again soon.
The component's state might be expensive to re-construct.
When `ngIf` becomes `true` again, Angular recreates the component and its subtree.
Angular runs every component's initialization logic again. That could be expensive ... as when
a component re-fetches data that had been in memory just moments ago.
.l-sub-section
:marked
*Design thought*: minimize initialization effort and consider caching state in a
companion service.
:marked
Although there are pros and cons to each approach,
in general it is best to use `ngIf` to remove unwanted components rather than
hide them.
**These same considerations apply to every structural directive, whether built-in or custom.**
We should ask ourselves &mdash; and the users of our directives &mdash; to think carefully
about the consequences of adding and removing elements and of creating and destroying components.
Let's see these dynamics at work. For fun, we'll stack the deck *against*
our recommendation and consider a component called `heavy-loader` that
***pretends*** to load a ton of data when initialized.
We'll display two instances of the component. We toggle the visibility of the first one with CSS.
We toggle the second into and out of the DOM with `ngIf`.
+makeTabs(
`structural-directives/ts/app/structural-directives.component.html,
structural-directives/ts/app/heavy-loader.component.ts`,
'message-log,',
'template (excerpt), heavy-loader.component.ts')
:marked
We also log when a component is created or destroyed
using the built-in `ngOnInit` and `ngOnDestroy` [lifecycle hooks](lifecycle-hooks.html).
Here it is in action:
figure.image-display
img(src='/resources/images/devguide/structural-directives/heavy-loader-toggle.gif' alt="heavy loader toggle")
:marked
Both components are in the DOM at the start.
First we toggle the component's visibility repeatedly. The component never leaves the DOM.
When visible it's always the same instance and the log is quiet.
Then we toggle the second component with `ngIf`.
We create a new instance every time and the log shows that we're paying
a heavy price to create and destroy it.
If we really expected to "wink" the component like this, toggling visibility would be the better choice.
In most UIs, when we "close" a component we're unlikely see it again for a long time, if ever.
The `ngIf` would be preferred in that case.
<a id="template"></a>
.l-main-section
:marked
## The *&lt;template>* tag
Structural directives, like `ngIf`, do their magic by using the
[HTML 5 template tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).
Outside of an Angular app, the `<template>` tag's default CSS `display` property is `none`.
It's contents are ***invisible*** within
a hidden [document fragment](https://developer.mozilla.org/en/docs/Web/API/DocumentFragment).
Inside of an app, Angular ***removes*** the`<template>` tags and their children.
The contents are gone &mdash; but not forgotten as we'll see soon.
We can confirm these effects by wrapping the middle "hip" of the phrase "Hip! Hip! Hooray!" within a `<template>` tag.
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'template-tag')(format=".")
:marked
The display is a 'Hip! Hooray!', short of perfect enthusiasm. The DOM effects are different when Angular is in control.
figure.image-display
img(src='/resources/images/devguide/structural-directives/template-in-out-of-a2.png' alt="template outside angular")
:marked
Evidently Angular replaces the `<template>` tag and its contents with empty `<script>` tags.
That's just its default behavior.
It can do something different as we saw when applying a variety of `ngSwitch` directives to `<template>` tags:
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngSwitch')(format=".")
:marked
When one of those `ngSwitch` conditions is true, Angular inserts the template's content into the DOM.
What does this have to do with `ngIf` and `ngFor`? We didn't use a `<template>` tag with those directives.
<a id="asterisk"></a>
.l-main-section
:marked
## The asterisk (\*) effect
Here are those directives again. See the difference?
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'asterisk')(format=".")
:marked
We're prefixing these directive names with an asterisk (\*).
The asterisk is "syntactic sugar". It simplifies `ngIf` and `ngFor` for both the writer and the reader.
Under the hood, Angular replaces the asterisk version with a more verbose `<template>` form.
The next two `ngIf` examples are effectively the same and we may write in either style:
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf-template')(format=".")
:marked
Most of us would rather write in style (A).
It's worth knowing that Angular expands style (A) into style (B).
It moves the paragraph and its contents inside a `<template>` tag.
It moves the directive up to the `<template>` tag where it becomes a property binding,
surrounded in square brackets. The boolean value of the host component's `condition` property
determines whether the templated content is displayed or not.
Angular transforms `*ngFor` in a similar manner:
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngFor-template')(format=".")
:marked
The basic pattern is the same:&nbsp; create a `<template>`, relocate the content,
and move the directive onto the `<template>`.
There are extra nuances stemming from
Angular's [ngFor micro-syntax](template-syntax.html#ngForMicrosyntax) which expands
into an additional `ngForOf` property binding (the iterable) and
the `hero` template input variable (the current item in each iteration).
<a id="unless"></a>
.l-main-section
:marked
## Make a structural directive
Let's write our own structural directive, an `Unless` directive, the not-so-evil twin of `ngIf`.
Unlike `ngIf` which displays the template content when `true`,
our directive displays the content when the condition is ***false***.
block unless-intro
:marked
Creating a directive is similar to creating a component.
* import the `Directive` decorator.
* add a CSS **attribute selector** (in brackets) that identifies our directive.
* specify the name of the public `input` property for binding
(typically the name of the directive itself).
* apply the decorator to our implementation class.
Here is how we begin:
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-declaration', 'unless.directive.ts (excerpt)')(format=".")
.l-sub-section
:marked
### Selector brackets [&nbsp;]
The CSS syntax for selecting an attribute is a name in square brackets.
We surround our directive name in square brackets. See *Directive configuration* on the
[cheatsheet](cheatsheet.html).
### Selector name prefixes
We recommend picking a selector name with a prefix to ensure
that it cannot conflict with any standard HTML attribute, now or in the future.
We do **not** prefix our `unless` directive name with **`ng`**.
That prefix belongs to Angular and
we don't want to confuse our directives with their directives.
Our prefix is `my`.
:marked
We'll need access to the template *and* something that can render its contents.
We access the template with a `TemplateRef`. The renderer is a `ViewContainerRef`.
We inject both into our constructor as private variables.
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-constructor')(format=".")
:marked
The consumer of our directive will bind a boolean value to our directive's `myUnless` input property.
The directive adds or removes the template based on that value.
Let's add the `myUnless` property now as a setter-only property.
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-set')(format=".")
.l-sub-section
:marked
The `@Input()` annotation marks this property as an input for the directive.
:marked
Nothing fancy here: if the condition is false,
we render the template, otherwise we clear the element content.
The end result should look like this:
+makeExample('structural-directives/ts/app/unless.directive.ts', null, 'unless.directive.ts')
:marked
Now we add it to the `directives`array of the host component and try it.
First we add some test HTML to the template:
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'myUnless')(format=".")
:marked
We run it and it behaves as expected, doing the opposite of `ngIf`.
When `condition` is `true`, the top paragraph is removed (replaced by `<script>` tags) and the bottom paragraph appears.
figure.image-display
img(src='/resources/images/devguide/structural-directives/myUnless-is-true.png' alt="myUnless is true" )
:marked
Our `myUnless` directive is dead simple. Surely we left something out.
Surely `ngIf` is more complex?
[Look at the source code](https://github.com/angular/angular/blob/master/modules/%40angular/common/src/directives/ng_if.ts).
It's well documented and we shouldn't be shy
about consulting the source when we want to know how something works.
`ngIf` isn't much different! There are a few
additional checks to improve performance (don't clear or recreate the
view unless necessary) but otherwise it's much the same.
.l-main-section
:marked
## Wrap up
Here is the pertinent source for this chapter.
+makeTabs(`
structural-directives/ts/app/unless.directive.ts,
structural-directives/ts/app/heavy-loader.component.ts,
structural-directives/ts/app/structural-directives.component.ts,
structural-directives/ts/app/structural-directives.component.html
`,
null,
`unless.directive.ts,
heavy-loader.component.ts,
structural-directives.component.ts,
structural-directives.component.html
`)
:marked
We learned that we can manipulate our HTML layout with
structural directives like `ngFor` and `ngIf` and we
wrote our own structural directive, `myUnless`, to do something similar.
Angular offers more sophisticated techniques for managing layout
such as *structural components* that can take external content
and incorporate that content within their own templates.
Tab and tab pane controls are good examples.
We'll learn about structural components in a future chapter.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,582 @@
block includes
include _util-fns
- var _Install = 'Install'
- var _prereq = 'Node.js'
- var _angular_browser_uri = '@angular/platform-browser-dynamic'
- var _angular_core_uri = '@angular/core'
:marked
Our QuickStart goal is to build and run a super-simple
Angular 2 application in #{_Lang}, and
establish a development environment for the remaining documentation samples
that also can be the foundation for real world applications.
.callout.is-helpful
header Don't want #{_Lang}?
p.
Although we're getting started in #{_Lang}, you can also write Angular 2 apps
in #{_docsFor == 'ts' ? 'Dart' : 'TypeScript'} and JavaScript.
Just select either of those languages from the combo-box in the banner.
:marked
# Try it!
Try the <live-example></live-example> which loads the sample app
<span if-docs="ts">
in <a href="http://plnkr.co/" title="Plunker" target="_blank">plunker</a>
</span>
and displays the simple message:
figure.image-display
img(src='/resources/images/devguide/quickstart/my-first-app.png' alt="Output of QuickStart app")
:marked
# Build this app!
- [Prerequisite](#prereq): Install #{_prereq}
- [Step 1](#create-and-configure): Create the apps project folder and
define package dependencies and special project setup
- [Step 2](#root-component): Create the apps Angular root component
- [Step 3](#main): Add <span ngio-ex>main.ts</span>, identifying the root component to Angular
- [Step 4](#index): Add `index.html`, the web page that hosts the application
- [Step 5](#build-and-run): Build and run the app
- [Make some changes to the app](#make-some-changes)
- [Wrap up](#wrap-up)
.l-main-section
h2#prereq Prerequisite: #{_prereq}
block setup-tooling
:marked
Install **[Node.js® and npm](https://nodejs.org/en/download/)**
if they are not already on your machine.
.l-sub-section
:marked
**Verify that you are running at least node `v4.x.x` and npm `3.x.x`**
by running `node -v` and `npm -v` in a terminal/console window.
Older versions produce errors.
block download-source
.l-main-section
.callout.is-helpful
header Download the source
:marked
Instead of following each step of these instructions, we can
[download the QuickStart source](https://github.com/angular/quickstart/blob/master/README.md)
from github and follow its brief instructions.
.l-main-section
button(class="verbose off md-primary md-button md-ink-ripple", type="button", onclick="verbose(false)").
Hide explanations
button(class="verbose on md-primary md-button md-ink-ripple", type="button", onclick="verbose(true)").
View explanations
.l-verbose-section
:marked
*Explanations* describe the concepts and reasons behind the instructions.
Explanations have a thin border on the left like *this* block of text.
Click *Hide Explanations* to show only the instructions.
Click *View Explanations* to see everything again.
.l-sub-section
:marked
We'll see many code blocks as we build the QuickStart app. They're all easy to copy and paste:
code-example(format="nocode").
Click the glyph on the right to copy code snippets to the clipboard ==>
.l-main-section
h2#create-and-configure Step 1: Create and configure the project
- var _package_and_config_files = _docsFor == 'dart' ? 'pubspec.yaml' : 'package definition and configuration files'
:marked
In this step we:
* [(a) Create the project folder](#create-the-project-folder)
* [(b) Add #{_package_and_config_files}](#add-config-files)
* [(c) #{_Install} packages](#install-packages)
h3 (a) Create the project folder
- var _ = _docsFor == 'dart' ? '_' : '-';
code-example(language="sh").
mkdir angular2!{_}quickstart
cd angular2!{_}quickstart
h3#add-config-files (b) Add #{_package_and_config_files}
block package-and-config-files
- var _tsconfigUri = 'guide/typescript-configuration.html#tsconfig'
- var _typingsUri = 'guide/typescript-configuration.html#!#typings'
p Add the following package definition and configuration files to the project folder:
ul
li.
#[b package.json] lists packages the QuickStart app depends on and
defines some useful scripts.
See #[a(href="guide/npm-packages.html") Npm Package Configuration] for details.
li.
#[b tsconfig.json] is the TypeScript compiler configuration file.
See #[a(href="#{_tsconfigUri}") TypeScript Configuration] for details.
li.
#[b typings.json] identifies TypeScript definition files.
See #[a(href="#{_typingsUri}") TypeScript Configuration] for details.
li.
#[b systemjs.config.js], the SystemJS configuration file.
See discussion #[a(href="#systemjs") below].
a#config-files
+makeTabs(`
quickstart/ts/package.1.json,
quickstart/ts/tsconfig.1.json,
quickstart/ts/typings.1.json,
quickstart/ts/systemjs.config.1.js
`, '', `
package.json,
tsconfig.json,
typings.json,
systemjs.config.js
`)
h3#install-packages (c) #{_Install} packages
block install-packages
:marked
We install the packages listed in `package.json` using `npm`. Enter the
following command in a terminal window (command window in Windows):
code-example(language="sh").
npm install
.l-sub-section
:marked
The `typings` folder could not show up after `npm install`. If so, please install them manually.
code-example(language="sh").
npm run typings install
.alert.is-important
:marked
Scary <span style="color:red; font-weight: bold">error messages in red</span> may appear **during** install.
The install typically recovers from these errors and finishes successfully.
.l-verbose-section(class="l-verbose-inherit")
:marked
#### npm errors and warnings
All is well if there are no console messages starting with `npm ERR!` *at the end* of **npm install**.
There might be a few `npm WARN` messages along the way &mdash; and that is perfectly fine.
We often see an `npm WARN` message after a series of `gyp ERR!` messages.
Ignore them. A package may try to recompile itself using `node-gyp`.
If the recompile fails, the package recovers (typically with a pre-built version)
and everything works.
Just make sure there are no `npm ERR!` messages at the end of `npm install`.
.l-verbose-section
:marked
#### Adding the libraries and packages we need with *npm*
Angular application developers rely on the _[npm](https://docs.npmjs.com)_
package manager to install the libraries and packages their apps require.
The Angular team recommends the starter-set of packages specified in the
`dependencies` and `devDependencies` sections.
See the [npm packages](guide/npm-packages.html) chapter for details.
#### Helpful scripts
We've included a number of npm scripts in our suggested `package.json` to handle common development tasks:
+makeJson('quickstart/ts/package.1.json',{ paths: 'scripts'}, 'package.json (scripts)')(format=".")
:marked
We execute most npm scripts in the following way: `npm run` followed by a *script-name*.
Some commands (such as `start`) don't require the `run` keyword.
Here's what these scripts do:
* `npm start` - runs the compiler and a server at the same time, both in "watch mode"
* `npm run tsc` - runs the TypeScript compiler once
* `npm run tsc:w` - runs the TypeScript compiler in watch mode;
the process keeps running, awaiting changes to TypeScript files and recompiling when it sees them
* `npm run lite` - runs the <a href="https://www.npmjs.com/package/lite-server" target="_blank">lite-server</a>,
a light-weight, static file server with excellent support for Angular apps that use routing
* `npm run typings` - runs the [*typings* tool](#{_typingsUri}) separately
* `npm run postinstall` - called by *npm* automatically *after* it successfully completes package installation.
This script installs the [TypeScript definition files](#{_typingsUri}) defined in `typings.json`
:marked
**We're all set.** Let's write some code.
.l-main-section
h2#root-component Step 2: Our first Angular component
:marked
Let's create a folder to hold our application and add a super-simple Angular component.
**Create #{_an} #{_appDir} subfolder** off the project root directory:
code-example.
mkdir #{_appDir}
a#app-component
p.
#[b Create the component file]
#[code #[+adjExPath('app/app.component.ts')]] (in this newly created directory) with the following content:
+makeExample('app/app.component.ts')
.l-verbose-section
:marked
### AppComponent is the root of the application
Every Angular app has at least one **root component**, conventionally named `AppComponent`,
that hosts the client user experience.
Components are the basic building blocks of Angular applications.
A component controls a portion of the screen &mdash; a *view* &mdash; through its associated template.
This QuickStart has only one, extremely simple component.
But it has the essential structure of every component we'll ever write:
* One or more [import](#component-import)
statements to reference the things we need.
* A [@Component #{_decorator}](#component-decorator)
that tells Angular what template to use and how to create the component.
* A [component class](#component-class)
that controls the appearance and behavior of a view through its template.
a#component-import
:marked
### Import
Angular apps are modular. They consist of many files each dedicated to a purpose.
Angular itself is modular. It is a collection of library modules
each made up of several, related features that we'll use to build our application.
When we need something from a module or library, we import it.
Here we import the Angular 2 core so that our component code can have access to
the `@Component` #{_decorator}.
+makeExcerpt('app/app.component.ts', 'import')
h3#component-decorator @Component #{_decorator}
+ifDocsFor('ts')
:marked
`Component` is a *decorator function* that takes a *metadata object* as argument.
We apply this function to the component class by prefixing the function with the
**@** symbol and invoking it with a metadata object, just above the class.
:marked
`@Component` is #{_a} *#{_decorator}* that allows us to associate *metadata* with the
component class.
The metadata tells Angular how to create and use this component.
+makeExcerpt('app/app.component.ts', 'metadata')
block annotation-fields
:marked
This particular metadata object has two fields, a `selector` and a `template`.
:marked
The **selector** specifies a simple CSS selector for an HTML element that represents the component.
>The element for this component is named `my-app`.
Angular creates and displays an instance of our `AppComponent`
wherever it encounters a `my-app` element in the host HTML.
The **template** specifies the component's companion template,
written in an enhanced form of HTML that tells Angular how to render this component's view.
>Our template is a single line of HTML announcing "*My First Angular 2 App*".
>A more advanced template could contain data bindings to component properties
and might identify other application components which have their own templates.
These templates might identify yet other components.
In this way an Angular application becomes a tree of components.
:marked
### Component class
At the bottom of the file is an empty, do-nothing class named `AppComponent`.
+makeExcerpt('app/app.component.ts', 'class')
:marked
When we're ready to build a substantive application,
we can expand this class with properties and application logic.
Our `AppComponent` class is empty because we don't need it to do anything in this QuickStart.
+ifDocsFor('ts')
:marked
We **export** `AppComponent` so that we can **import** it elsewhere in our application,
as we'll see when we create `main.ts`.
.l-main-section
h2#main Step 3: Add #[code #[+adjExPath('main.ts')]]
block create-main
p.
Now we need something to tell Angular to load the root component.
Create the file #[code #[+adjExPath('app/main.ts')]] with the following content:
+makeExample('app/main.ts')
.l-verbose-section
:marked
We import the two things we need to launch the application:
1. Angular's browser `bootstrap` function
1. The application root component, `AppComponent`.
Then we call `bootstrap` with `AppComponent`.
### Bootstrapping is platform-specific
Notice that we import the `bootstrap` function from `#{_angular_browser_uri}`,
not `#{_angular_core_uri}`.
Bootstrapping isn't core because there isn't a single way to bootstrap the app.
True, most applications that run in a browser call the bootstrap function from
this library.
But it is possible to load a component in a different environment.
We might load it on a mobile device with [Apache Cordova](https://cordova.apache.org/) or [NativeScript](https://www.nativescript.org/).
We might wish to render the first page of our application on the server
to improve launch performance or facilitate
[SEO](http://www.google.com/webmasters/docs/search-engine-optimization-starter-guide.pdf).
These targets require a different kind of bootstrap function that we'd import from a different library.
### Why create separate *<span ngio-ex>main.ts</span>* and app component files?
Both <span ngio-ex>main.ts</span> and the app component files are tiny.
This is just a QuickStart.
We could have merged these two files into one
and spared ourselves some complexity.
We'd rather demonstrate the proper way to structure an Angular application.
App bootstrapping is a separate concern from presenting a view.
Mixing concerns creates difficulties down the road.
We might launch the `AppComponent` in multiple environments with different bootstrappers.
Testing the component is much easier if it doesn't also try to run the entire application.
Let's make the small extra effort to do it *the right way*.
.l-main-section
h2#index Step 4: Add #[code index.html]
:marked
In the *#{_indexHtmlDir}* folder
create an `index.html` file and paste the following lines into it:
+makeExample('index.html')
.l-verbose-section
:marked
The `index.html` file defines the web page that hosts the application.
block index-html-commentary-for-ts
:marked
The noteworthy sections of HTML are:
1. The JavaScript [libraries](#libraries)
2. Configuration file for [SystemJS](#systemjs), and a script
where we import and run the `app` module which refers to the `main` file that we just wrote.
3. The [`<my-app>`](#my-app) tag in the `<body>` which is *where our app lives!*
:marked
### Libraries
We loaded the following scripts
+makeExcerpt('index.html', 'libraries')
:marked
We begin with `core-js`'s ES2015/ES6 shim which monkey patches the global context (window) with essential features of ES2015 (ES6).
Next are the polyfills for Angular2, `zone.js` and `reflect-metadata`.
Then the [SystemJS](#systemjs) library for module loading.
We'll make different choices as we gain experience and
become more concerned about production qualities such as
load times and memory footprint.
h3#systemjs SystemJS
:marked
QuickStart uses <a href="https://github.com/systemjs/systemjs" target="_blank">SystemJS</a>
to load application and library modules. [Earlier](#add-config-files) we
added the `systemjs.config.js` file to the project root.
There are alternatives that work just fine including the well-regarded
[webpack](guide/webpack.html).
SystemJS happens to be a good choice.
But we want to be clear that it was a *choice* and not a *preference*.
All module loaders require configuration and all loader configuration
becomes complicated rather quickly as soon as the file structure diversifies and
we start thinking about building for production and performance.
We suggest becoming well-versed in the loader of your choice.
Learn more about SystemJS configuration
<a href="https://github.com/systemjs/systemjs/blob/master/docs/config-api.md" target="_blank">here</a>.
With those cautions in mind, what are we doing in the
QuickStart [`systemjs.config.js` configuration file we added earlier](#config-files)?
First, we create a map to tell SystemJS where to look when we import some module.
Then, we register all our packages to SystemJS:
all the project dependencies and our application package, `app`.
.l-sub-section
:marked
Our QuickStart doesn't use all of the listed packages
but any substantial application will want many of them
and all of the listed packages are required by at least one of the documentation samples.
There is no runtime harm in listing packages that we don't need as they will only be loaded when requested.
:marked
The `app` package tells SystemJS what to do when it sees a request for a
module from the `app/` folder.
Our QuickStart makes such requests when one of its
application TypeScript files has an import statement like this:
+makeExcerpt('app/main.ts', 'import')
:marked
Notice that the module name (after `from`) does not mention a filename extension.
In the configuration we tell SystemJS to default the extension to `js`, a JavaScript file.
That makes sense because we transpile TypeScript to JavaScript
*before* running the application.
.l-sub-section
:marked
#### Transpiling in the browser
In the live example on plunker we transpile (AKA compile) to JavaScript in the browser
on the fly. _That's fine for a demo_.
**Do not transpile in the browser during development or for production**.
We strongly recommend transpiling (AKA compiling) to JavaScript during a build phase
before running the application for several reasons including:
* We see compiler warnings and errors that are hidden from us in the browser.
* Precompilation simplifies the module loading process and
it's much easier to diagnose problems when this is a separate, external step.
* Precompilation means a faster user experience because the browser doesn't waste time compiling.
* We iterate development faster because we only recompile changed files.
We notice the difference as soon as the app grows beyond a handful of files.
* Precompilation fits into a continuous integration process of build, test, deploy.
:marked
The `System.import` call tells SystemJS to import the `main` file
(`main.js` ... after transpiling `main.ts`, remember?);
`main` is where we tell Angular to launch the application.
We also catch and log launch errors to the console.
All other modules are loaded upon request
either by an import statement or by Angular itself.
### *&lt;my-app&gt;*
a(id="my-app")
:marked
When Angular calls the `bootstrap` function in <span ngio-ex>main.ts</span>, it reads the `AppComponent`
metadata, finds the `my-app` selector, locates an element tag named `my-app`,
and renders our application's view between those tags.
:marked
### Add some style
Styles aren't essential but they're nice, and `index.html` assumes we have
a stylesheet called `styles.css`.
Create a `styles.css` file in the *#{_indexHtmlDir}* folder and start styling, perhaps with the minimal
styles shown below. For the full set of master styles used by the documentation samples,
see [styles.css](https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css).
+makeExcerpt('styles.1.css')
.l-main-section
h2#build-and-run Step 5: Build and run the app!
block run-app
:marked
Open a terminal window and enter this command:
code-example.
npm start
:marked
That command runs two parallel node processes
1. The TypeScript compiler in watch mode
1. A static server called **lite-server** that loads `index.html` in a browser
and refreshes the browser when application files change
In a few moments, a browser tab should open and display
figure.image-display
img(src='/resources/images/devguide/quickstart/my-first-app.png' alt="Output of QuickStart app")
:marked
**Great job!**
block build-app
//- Nothing for ts.
:marked
## Make some changes
Try changing the message to "My SECOND Angular 2 app".
block server-watching
:marked
The TypeScript compiler and `lite-server` are watching.
They should detect the change, recompile the TypeScript into JavaScript,
refresh the browser, and display the revised message.
It's a nifty way to develop an application!
We close the terminal window when we're done to terminate both the compiler and the server.
.l-main-section
:marked
# Wrap up
Our final project folder structure looks like this:
block project-file-structure
.filetree
.file angular2-quickstart
.children
.file app
.children
.file app.component.ts
.file main.ts
.file node_modules ...
.file typings ...
.file index.html
.file package.json
.file styles.css
.file systemjs.config.js
.file tsconfig.json
.file typings.json
:marked
Here are the file contents:
block project-files
+makeTabs(`
quickstart/ts/app/app.component.ts,
quickstart/ts/app/main.ts,
quickstart/ts/index.html,
quickstart/ts/package.1.json,
quickstart/ts/tsconfig.1.json,
quickstart/ts/typings.1.json,
quickstart/ts/styles.1.css,
quickstart/ts/systemjs.config.1.js`
,null,
`app/app.component.ts,
app/main.ts,
index.html,
package.json,
tsconfig.json,
typings.json,
styles.css,
systemjs.config.js`)
.l-main-section
:marked
## What next?
Our first application doesn't do much. It's basically "Hello, World" for Angular 2.
We kept it simple in our first pass: we wrote a little Angular component,
created a simple `index.html`, and launched with a
static file server. That's about all we'd expect to do for a "Hello, World" app.
**We have greater ambitions!**
block what-next-ts-overhead
:marked
The good news is that the overhead of setup is (mostly) behind us.
We'll probably only touch the `package.json` to update libraries.
We'll likely open `index.html` only if we need to add a library or some css stylesheets.
:marked
We're about to take the next step and build a small application that
demonstrates the great things we can build with Angular 2.
Join us on the [Tour of Heroes Tutorial](./tutorial)!

View File

@ -0,0 +1,632 @@
- var _example = 'toh-6';
block includes
include ../_util-fns
- var _Http = 'Http'; // Angular `Http` library name.
- var _Angular_Http = 'Angular <code>Http</code>'
- var _Angular_http_library = 'Angular HTTP library'
- var _HTTP_PROVIDERS = 'HTTP_PROVIDERS'
- var _JSON_stringify = 'JSON.stringify'
:marked
# Getting and Saving Data with HTTP
Our stakeholders appreciate our progress.
Now they want to get the hero data from a server, let users add, edit, and delete heroes,
and save these changes back to the server.
In this chapter we teach our application to make the corresponding HTTP calls to a remote server's web API.
Run the <live-example></live-example> for this part.
.l-main-section
:marked
## Where We Left Off
In the [previous chapter](toh-pt5.html), we learned to navigate between the dashboard and the fixed heroes list, editing a selected hero along the way.
That's our starting point for this chapter.
block start-server-and-watch
:marked
### Keep the app transpiling and running
Open a terminal/console window and enter the following command to
start the TypeScript compiler, start the server, and watch for changes:
code-example(language="bash").
npm start
:marked
The application runs and updates automatically as we continue to build the Tour of Heroes.
.l-main-section#http-providers
h1 Providing HTTP Services
block http-library
:marked
`Http` is ***not*** a core Angular module.
It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`,
shipped in a separate script file as part of the Angular npm package.
Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it.
:marked
### Register (provide) *HTTP* services
block http-providers
:marked
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services.
:marked
We should be able to access `!{_Http}` services from anywhere in the application.
So we register them in the `bootstrap` call of <span ngio-ex>main.ts</span> where we
launch the application and its root `AppComponent`.
+makeExcerpt('app/main.ts','v1')
:marked
Notice that we supply `!{_HTTP_PROVIDERS}` in !{_an} !{_array} as the second parameter to the `bootstrap` method.
This has the same effect as the `providers` !{_array} in `@Component` !{_decorator}.
.l-main-section
:marked
## Simulating the web API
We generally recommend registering application-wide services in the root `AppComponent` *providers*.
Here we're registering in `main` for a special reason.
Our application is in the early stages of development and far from ready for production.
We don't even have a web server that can handle requests for heroes.
Until we do, *we'll have to fake it*.
We're going to *trick* the HTTP client into fetching and saving data from
a mock service, the *in-memory web API*.
The application itself doesn't need to know and shouldn't know about this.
So we'll slip the in-memory web API into the configuration *above* the `AppComponent`.
Here is a version of `main` that performs this trick
+makeExcerpt('app/main.ts', 'final')
block backend
:marked
We're replacing the default `XHRBackend`, the service that talks to the remote server,
with the in-memory web API service after priming it as follows:
+makeExample('app/in-memory-data.service.ts', 'init')
p This file replaces the #[code #[+adjExPath('mock-heroes.ts')]] which is now safe to delete.
block dont-be-distracted-by-backend-subst
.alert.is-helpful
:marked
This chapter is an introduction to the !{_Angular_http_library}.
Please don't be distracted by the details of this backend substitution. Just follow along with the example.
Learn more later about the in-memory web API in the [HTTP client chapter](../guide/server-communication.html#!#in-mem-web-api).
Remember, the in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
Skip it when you have a real web API server.
.l-main-section
:marked
## Heroes and HTTP
Look at our current `HeroService` implementation
+makeExcerpt('toh-4/ts/app/hero.service.ts (old getHeroes)', 'get-heroes')
:marked
We returned a !{_Promise} resolved with mock heroes.
It may have seemed like overkill at the time, but we were anticipating the
day when we fetched heroes with an HTTP client and we knew that would have to be an asynchronous operation.
That day has arrived! Let's convert `getHeroes()` to use HTTP:
+makeExcerpt('app/hero.service.ts (new constructor and revised getHeroes)', 'getHeroes')
:marked
### HTTP !{_Promise}
We're still returning a !{_Promise} but we're creating it differently.
block get-heroes-details
:marked
The Angular `http.get` returns an RxJS `Observable`.
*Observables* are a powerful way to manage asynchronous data flows.
We'll learn about [Observables](#observables) later in this chapter.
For *now* we get back on familiar ground by immediately by
converting that `Observable` to a `Promise` using the `toPromise` operator.
+makeExcerpt('app/hero.service.ts', 'to-promise', '')
:marked
Unfortunately, the Angular `Observable` doesn't have a `toPromise` operator ... not out of the box.
The Angular `Observable` is a bare-bones implementation.
There are scores of operators like `toPromise` that extend `Observable` with useful capabilities.
If we want those capabilities, we have to add the operators ourselves.
That's as easy as importing them from the RxJS library like this:
+makeExcerpt('app/hero.service.ts', 'rxjs', '')
:marked
### Extracting the data in the *then* callback
In the *promise*'s `then` callback we call the `json` method of the http `Response` to extract the
data within the response.
+makeExcerpt('app/hero.service.ts', 'to-data', '')
:marked
That response JSON has a single `data` property.
The `data` property holds the !{_array} of *heroes* that the caller really wants.
So we grab that !{_array} and return it as the resolved !{_Promise} value.
.alert.is-important
:marked
Pay close attention to the shape of the data returned by the server.
This particular *in-memory web API* example happens to return an object with a `data` property.
Your API might return something else.
Adjust the code to match *your web API*.
:marked
The caller is unaware of these machinations. It receives a !{_Promise} of *heroes* just as it did before.
It has no idea that we fetched the heroes from the (mock) server.
It knows nothing of the twists and turns required to convert the HTTP response into heroes.
Such is the beauty and purpose of delegating data access to a service like this `HeroService`.
:marked
### Error Handling
At the end of `getHeroes()` we `catch` server failures and pass them to an error handler:
+makeExcerpt('app/hero.service.ts', 'catch', '')
:marked
This is a critical step!
We must anticipate HTTP failures as they happen frequently for reasons beyond our control.
+makeExcerpt('app/hero.service.ts', 'handleError', '')
- var rejected_promise = _docsFor == 'dart' ? 'propagated exception' : 'rejected promise';
:marked
In this demo service we log the error to the console; we should do better in real life.
We've also decided to return a user friendly form of the error to
the caller in a !{rejected_promise} so that the caller can display a proper error message to the user.
### !{_Promise}s are !{_Promise}s
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`.
.l-main-section
:marked
## Add, Edit, Delete
Our stakeholders are incredibly pleased with the added flexibility from the API integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes.
We'll complete `HeroService` by creating `post`, `put` and `delete` methods to meet our new requirements.
:marked
### Post
We will be using `post` to add new heroes. Post requests require a little bit more setup than Get requests:
+makeExcerpt('app/hero.service.ts', 'post')
:marked
For Post requests we create a header and set the content type to `application/json`. We'll call `!{_JSON_stringify}` before we post to convert the hero object to a string.
### Put
Put will be used to update an individual hero. Its structure is very similar to Post requests. The only difference is that we have to change the URL slightly by appending the id of the hero we want to update.
+makeExcerpt('app/hero.service.ts', 'put')
:marked
### Delete
Delete will be used to delete heroes and its format is like `put` except for the function name.
+makeExcerpt('app/hero.service.ts', 'delete')
:marked
We add a `catch` to handle errors for all three methods.
:marked
### Save
We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public API and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add.
+makeExcerpt('app/hero.service.ts', 'save')
:marked
After these additions our `HeroService` looks like this:
+makeExample('app/hero.service.ts')
.l-main-section
:marked
## Updating Components
Loading heroes using `Http` required no changes outside of `HeroService`, but we added a few new features as well.
In the following section we will update our components to use our new methods to add, edit and delete heroes.
block hero-detail-comp-extra-imports-and-vars
:marked
Before we can add those methods, we need to initialize some variables with their respective imports.
+makeExcerpt('app/hero-detail.component.ts ()', 'variables-imports')
block hero-detail-comp-updates
:marked
### Add/Edit in the *HeroDetailComponent*
We already have `HeroDetailComponent` for viewing details about a specific hero.
Add and Edit are natural extensions of the detail view, so we are able to reuse `HeroDetailComponent` with a few tweaks.
The original component was created to render existing data, but to add new data we have to initialize the `hero` property to an empty `Hero` object.
+makeExcerpt('app/hero-detail.component.ts', 'ngOnInit')
:marked
In order to differentiate between add and edit we are adding a check to see if an id is passed in the URL. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property.
:marked
Add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`.
+makeExcerpt('app/hero-detail.component.ts', 'save')
block hero-detail-comp-save-and-goback
:marked
The same save method is used for both add and edit since `HeroService` will know when to call `post` vs `put` based on the state of the `Hero` object.
After we save a hero, we redirect the browser back to the previous page using the `goBack()` method.
+makeExcerpt('app/hero-detail.component.ts', 'goBack')
:marked
Here we call `emit` to notify that we just added or modified a hero. `HeroesComponent` is listening for this notification and will automatically refresh the list of heroes to include our recent updates.
.l-sub-section
:marked
The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a>
:marked
Here is `HeroDetailComponent` with its new save button and the corresponding HTML.
figure.image-display
img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button")
+makeExcerpt('app/hero-detail.component.html', 'save')
:marked
### Add/Delete in the *HeroesComponent*
We'll be reporting propagated HTTP errors, let's start by adding the following
field to the `HeroesComponent` class:
+makeExcerpt('app/heroes.component.ts', 'error', '')
:marked
The user can *add* a new hero by clicking a button and entering a name.
block add-new-hero-via-detail-comp
:marked
When the user clicks the *Add New Hero* button, we display the `HeroDetailComponent`.
We aren't navigating to the component so it won't receive a hero `id`;
as we noted above, that is the component's cue to create and present an empty hero.
- var _below = _docsFor == 'dart' ? 'before' : 'below';
:marked
Add the following to the heroes component HTML, just !{_below} the hero list (`<ul class="heroes">...</ul>`).
+makeExcerpt('app/heroes.component.html', 'add-and-error')
:marked
The first line will display an error message if there is any. The remaining HTML is for adding heroes.
The user can *delete* an existing hero by clicking a delete button next to the hero's name.
Add the following to the heroes component HTML right after the hero name in the repeated `<li>` tag:
+makeExcerpt('app/heroes.component.html', 'delete')
:marked
Add the following to the bottom of the `HeroesComponent` CSS file:
+makeExcerpt('app/heroes.component.css', 'additions')
:marked
Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions used in the template.
Let's start with *add*.
block heroes-comp-directives
:marked
We're using the `HeroDetailComponent` to capture the new hero information.
We have to tell Angular about that by importing the `HeroDetailComponent` and referencing it in the component metadata `directives` array.
+makeExcerpt('app/heroes.component.ts (HeroDetailComponent)', 'hero-detail-component')
.l-sub-section
:marked
These are the same lines that we removed in the previous [Routing](toh-pt5.html) chapter.
We didn't know at the time that we'd need the *HeroDetailComponent* again. So we tidied up.
Now we *must* put these lines back. If we don't, Angular will ignore the `<my-hero-detail>`
tag and pushing the *Add New Hero* button will have no visible effect.
:marked
Implement the click handler for the *Add New Hero* button.
+makeExcerpt('app/heroes.component.ts', 'addHero')
block heroes-comp-add
:marked
The `HeroDetailComponent` does most of the work. All we do is toggle an `*ngIf` flag that
swaps it into the DOM when we add a hero and removes it from the DOM when the user is done.
:marked
The *delete* logic is a bit trickier.
+makeExcerpt('app/heroes.component.ts', 'deleteHero')
:marked
Of course we delegate the persistence of hero deletion to the `HeroService`.
But the component is still responsible for updating the display.
So the *delete* method removes the deleted hero from the list.
block review
:marked
### Let's see it
Here are the fruits of labor in action:
figure.image-display
img(src='/resources/images/devguide/toh/toh-http.anim.gif' alt="Heroes List Editing w/ HTTP")
:marked
## !{_Observable}s
block observables-section-intro
:marked
Each `Http` method returns an `Observable` of HTTP `Response` objects.
Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
In this section we learn to return the `Observable` directly and discuss when and why that might be
a good thing to do.
### Background
An *observable* is a stream of events that we can process with array-like operators.
Angular core has basic support for observables. We developers augment that support with
operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library.
We'll see how shortly.
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller.
Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data.
When we receive the data, we're done.
A single result in the form of a promise is easy for the calling component to consume
and it helps that promises are widely understood by JavaScript programmers.
:marked
But requests aren't always "one and done". We may start one request,
then cancel it, and make a different request before the server has responded to the first request.
Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*.
It's easy with *!{_Observable}s* as we'll see.
### Search-by-name
We're going to add a *hero search* feature to the Tour of Heroes.
As the user types a name into a search box, we'll make repeated HTTP requests for heroes filtered by that name.
We start by creating `HeroSearchService` that sends search queries to our server's web api.
+makeExample('app/hero-search.service.ts')
:marked
The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one
in the `HeroService`, although the URL now has a query string.
<span if-docs="ts">Another notable difference: we no longer call `toPromise`,
we simply return the *observable* instead.</span>
### HeroSearchComponent
Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`.
The component template is simple &mdash; just a text box and a list of matching search results.
+makeExample('app/hero-search.component.html')
:marked
We'll also want to add styles for the new component.
+makeExample('app/hero-search.component.css')
:marked
As the user types in the search box, a *keyup* event binding calls the component's `search` method with the new search box value.
The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there.
But, as we'll soon see, the `heroes` property is now !{_an} *!{_Observable}* of hero !{_array}s, rather than just a hero !{_array}.
The `*ngFor` can't do anything with !{_an} `!{_Observable}` until we flow it through the `async` pipe (`AsyncPipe`).
The `async` pipe subscribes to the `!{_Observable}` and produces the !{_array} of heroes to `*ngFor`.
Time to create the `HeroSearchComponent` class and metadata.
+makeExample('app/hero-search.component.ts')
:marked
#### Search terms
Let's focus on the `!{_priv}searchTerms`:
+makeExcerpt('app/hero-search.component.ts', 'searchTerms', '')
block search-criteria-intro
:marked
A `Subject` is a producer of an _observable_ event stream;
`searchTerms` produces an `Observable` of strings, the filter criteria for the name search.
Each call to `search` puts a new string into this subject's _observable_ stream by calling `next`.
:marked
<a id="ngoninit"></a>
#### Initialize the _**heroes**_ property (_**ngOnInit**_)
<span if-docs="ts">A `Subject` is also an `Observable`.</span>
We're going to turn the stream
of search terms into a stream of `Hero` !{_array}s and assign the result to the `heroes` property.
+makeExcerpt('app/hero-search.component.ts', 'search', '')
:marked
If we passed every user keystroke directly to the `HeroSearchService`, we'd unleash a storm of HTTP requests.
Bad idea. We don't want to tax our server resources and burn through our cellular network data plan.
block observable-transformers
:marked
Fortunately, we can chain `Observable` operators to the string `Observable` that reduce the request flow.
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
before passing along the latest string. We'll never make requests more frequently than 300ms.
* `distinctUntilChanged` ensures that we only send a request if the filter text changed.
There's no point in repeating a request for the same search term.
* `switchMap` calls our search service for each search term that makes it through the `debounce` and `distinctUntilChanged` gauntlet.
It cancels and discards previous search observables, returning only the latest search service observable.
.l-sub-section
:marked
The [switchMap operator](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md)
(formerly known as "flatMapLatest") is very clever.
Every qualifying key event can trigger an http call.
Even with a 300ms pause between requests, we could have multiple http requests in flight
and they may not return in the order sent.
`switchMap` preserves the original request order while returning
only the observable from the most recent http call.
Results from prior calls are canceled and discarded.
We also short-circuit the http call and return an observable containing an empty array
if the search text is empty.
Note that _canceling_ the `HeroSearchService` observable won't actually abort a pending http request
until the service supports that feature, a topic for another day.
We are content for now to discard unwanted results.
:marked
* `catch` intercepts a failed observable.
Our simple example prints the error to the console; a real life application should do better.
Then we return an observable containing an empty array to clear the search result.
### Import RxJS operators
The RxJS operators are not available in Angular's base `Observable` implementation.
We have to extend `Observable` by *importing* them.
We could extend `Observable` with just the operators we need here by
including the pertinent `import` statements at the top of this file.
.l-sub-section
:marked
Many authorities say we should do just that.
:marked
We take a different approach in this example.
We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file.
+makeExample('app/rxjs-extensions.ts')
:marked
We load them all at once by importing `rxjs-extensions` in `AppComponent`.
+makeExcerpt('app/app.component.ts', 'rxjs-extensions')
:marked
### Add the search component to the dashboard
We add the hero search HTML element to the bottom of the `DashboardComponent` template.
+makeExample('app/dashboard.component.html')
:marked
And finally, we import the `HeroSearchComponent` and add it to the `directives` !{_array}.
+makeExcerpt('app/dashboard.component.ts', 'search')
:marked
Run the app again, go to the *Dashboard*, and enter some text in the search box.
At some point it might look like this.
figure.image-display
img(src='/resources/images/devguide/toh/toh-hero-search.png' alt="Hero Search Component")
.l-main-section
:marked
## Application structure and code
Review the sample source code in the <live-example></live-example> for this chapter.
Verify that we have the following structure:
block filetree
.filetree
.file angular2-tour-of-heroes
.children
.file app
.children
.file app.component.ts
.file app.component.css
.file app.routes.ts
.file dashboard.component.css
.file dashboard.component.html
.file dashboard.component.ts
.file hero.ts
.file hero-detail.component.css
.file hero-detail.component.html
.file hero-detail.component.ts
.file hero-search.component.html (new)
.file hero-search.component.css (new)
.file hero-search.component.ts (new)
.file hero-search.service.ts (new)
.file rxjs-operators.ts
.file hero.service.ts
.file heroes.component.css
.file heroes.component.html
.file heroes.component.ts
.file main.ts
.file in-memory-data.service.ts (new)
.file node_modules ...
.file typings ...
.file index.html
.file package.json
.file styles.css
.file systemjs.config.json
.file tsconfig.json
.file typings.json
.l-main-section
:marked
## Home Stretch
We are at the end of our journey for now, but we have accomplished a lot.
- We added the necessary dependencies to use HTTP in our application.
- We refactored `HeroService` to load heroes from a web API.
- We extended `HeroService` to support post, put and delete methods.
- We updated our components to allow adding, editing and deleting of heroes.
- We configured an in-memory web API.
- We learned how to use !{_Observable}s.
Here are the files we added or changed in this chapter.
block file-summary
+makeTabs(
`toh-6/ts/app/app.component.ts,
toh-6/ts/app/heroes.component.ts,
toh-6/ts/app/heroes.component.html,
toh-6/ts/app/heroes.component.css,
toh-6/ts/app/hero-detail.component.ts,
toh-6/ts/app/hero-detail.component.html,
toh-6/ts/app/hero.service.ts,
toh-6/ts/app/in-memory-data.service.ts`,
null,
`app.comp...ts,
heroes.comp...ts,
heroes.comp...html,
heroes.comp...css,
hero-detail.comp...ts,
hero-detail.comp...html,
hero.service.ts,
in-memory-data.service.ts`
)
+makeTabs(
`toh-6/ts/app/hero-search.service.ts,
toh-6/ts/app/hero-search.component.ts,
toh-6/ts/app/hero-search.component.html,
toh-6/ts/app/hero-search.component.css,
toh-6/ts/app/rxjs-operators.ts`,
null,
`hero-search.service.ts,
hero-search.component.ts,
hero-search.service.html,
hero-search.component.css,
rxjs-operators.ts`
)

44
scripts/refresh-cache.sh Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -e -o pipefail
BASE="public/docs/ts"
LATEST="$BASE/latest"
CACHE="$BASE/_cache"
FILES="
guide/architecture.jade
guide/attribute-directives.jade
guide/component-styles.jade
guide/dependency-injection.jade
guide/displaying-data.jade
guide/hierarchical-dependency-injection.jade
guide/lifecycle-hooks.jade
guide/pipes.jade
guide/security.jade
guide/server-communication.jade
guide/structural-directives.jade
guide/template-syntax.jade
quickstart.jade
tutorial/toh-pt6.jade"
function main() {
local allFound=true;
for f in $FILES; do
local srcPath="$LATEST/$f";
local destPath="$CACHE/$f";
local destDir=`dirname $destPath`;
if [[ -e $srcPath ]]; then
[[ -d "$destDir" ]] || (set -x; mkdir $destDir);
(set -x; cp $srcPath $destPath)
else
echo Cannot find $srcPath
allFound=false;
fi
done
[[ $allFound ]] || exit 1;
}
main;