docs(toh): copy edit and fixes in 4, 5, 6 and example (#3412)

This commit is contained in:
Kapunahele Wong 2017-03-23 18:47:55 -04:00 committed by Ward Bell
parent 56c8706262
commit a705a0ca3c
5 changed files with 110 additions and 94 deletions

View File

@ -3,7 +3,7 @@
// is solely for containing the transitory state of the imports. // is solely for containing the transitory state of the imports.
// #docregion added-imports // #docregion added-imports
// Keep the Input import for now, we'll remove it later: // Keep the Input import for now, you'll remove it later:
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common'; import { Location } from '@angular/common';

View File

@ -1,3 +1,4 @@
/* #docregion */
.selected { .selected {
background-color: #CFD8DC !important; background-color: #CFD8DC !important;
color: white; color: white;

View File

@ -92,7 +92,7 @@ code-example(language="sh" class="code-shell").
:marked :marked
### Getting hero data ### Getting hero data
Add a `getHeroes` method stub. Add a `getHeroes()` method stub.
+makeExample('toh-4/ts/src/app/hero.service.1.ts', 'getHeroes-stub', 'src/app/hero.service.ts (getHeroes stub)')(format=".") +makeExample('toh-4/ts/src/app/hero.service.1.ts', 'getHeroes-stub', 'src/app/hero.service.ts (getHeroes stub)')(format=".")
@ -117,7 +117,7 @@ code-example(language="sh" class="code-shell").
+makeExample('toh-4/ts/src/app/app.component.1.ts', 'heroes-prop', 'src/app/app.component.ts (heroes property)')(format=".") +makeExample('toh-4/ts/src/app/app.component.1.ts', 'heroes-prop', 'src/app/app.component.ts (heroes property)')(format=".")
:marked :marked
### Return mocked hero data ### Return mocked hero data
Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes` method. Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method.
The `HeroService` looks like this: The `HeroService` looks like this:
+makeExample('toh-4/ts/src/app/hero.service.1.ts', 'full', 'src/app/hero.service.ts')(format=".") +makeExample('toh-4/ts/src/app/hero.service.1.ts', 'full', 'src/app/hero.service.ts')(format=".")
@ -184,7 +184,7 @@ code-example(format="nocode").
The `AppComponent`, as well as its child components, can use that service to get hero data. The `AppComponent`, as well as its child components, can use that service to get hero data.
a#child-component a#child-component
:marked :marked
### *getHeroes* in the *AppComponent* ### *getHeroes()* in the *AppComponent*
The service is in a `heroService` private variable. The service is in a `heroService` private variable.
You could call the service and get the data in one line. You could call the service and get the data in one line.
@ -198,12 +198,12 @@ a#child-component
### The *ngOnInit* lifecycle hook ### The *ngOnInit* lifecycle hook
`AppComponent` should fetch and display hero data with no issues. `AppComponent` should fetch and display hero data with no issues.
You might be tempted to call the `getHeroes` method in a constructor, but You might be tempted to call the `getHeroes()` method in a constructor, but
a constructor should not contain complex logic, a constructor should not contain complex logic,
especially a constructor that calls a server, such as as a data access method. especially a constructor that calls a server, such as as a data access method.
The constructor is for simple initializations, like wiring constructor parameters to properties. The constructor is for simple initializations, like wiring constructor parameters to properties.
To have Angular call `getHeroes`, you can implement the Angular *ngOnInit lifecycle hook*. To have Angular call `getHeroes()`, you can implement the Angular *ngOnInit lifecycle hook*.
Angular offers interfaces for tapping into critical moments in the component lifecycle: Angular offers interfaces for tapping into critical moments in the component lifecycle:
at creation, after each change, and at its eventual destruction. at creation, after each change, and at its eventual destruction.
@ -221,7 +221,7 @@ code-example(format="nocode").
export class AppComponent implements OnInit {} export class AppComponent implements OnInit {}
:marked :marked
Write an `ngOnInit` method with the initialization logic inside. Angular will call it Write an `ngOnInit` method with the initialization logic inside. Angular will call it
at the right time. In this case, initialize by calling `getHeroes`. at the right time. In this case, initialize by calling `getHeroes()`.
+makeExcerpt('toh-4/ts/src/app/app.component.1.ts', 'ng-on-init', 'app/app.component.ts') +makeExcerpt('toh-4/ts/src/app/app.component.1.ts', 'ng-on-init', 'app/app.component.ts')
:marked :marked
The app should run as expected, showing a list of heroes and a hero detail view The app should run as expected, showing a list of heroes and a hero detail view
@ -231,15 +231,17 @@ code-example(format="nocode").
:marked :marked
## Async services and !{_Promise}s ## Async services and !{_Promise}s
The `HeroService` returns a list of mock heroes immediately; The `HeroService` returns a list of mock heroes immediately;
its `getHeroes` signature is synchronous. its `getHeroes()` signature is synchronous.
+makeExample('toh-4/ts/src/app/app.component.1.ts', 'get-heroes')(format=".") +makeExample('toh-4/ts/src/app/app.component.1.ts', 'get-heroes')(format=".")
:marked :marked
Eventually, the hero data will come from a remote server. Eventually, the hero data will come from a remote server.
When using the remote server, users don't have to wait for the server to respond When using a remote server, users don't have to wait for the server to respond;
and you aren't able to block the UI during the wait. additionally, you aren't able to block the UI during the wait.
:marked :marked
Instead, you can use *!{_Promise}s*, which is an asynchronous technique that changes the signature of the `getHeroes` method. To coordinate the view with the response,
you can use *!{_Promise}s*, which is an asynchronous
technique that changes the signature of the `getHeroes()` method.
### The hero service makes a !{_Promise} ### The hero service makes a !{_Promise}
@ -253,7 +255,7 @@ code-example(format="nocode").
[Exploring ES6](http://http://exploringjs.com/es6.html). [Exploring ES6](http://http://exploringjs.com/es6.html).
:marked :marked
Update the `HeroService` with this !{_Promise}-returning `getHeroes` method: Update the `HeroService` with this !{_Promise}-returning `getHeroes()` method:
+makeExample('toh-4/ts/src/app/hero.service.ts', 'get-heroes', 'src/app/hero.service.ts (excerpt)')(format=".") +makeExample('toh-4/ts/src/app/hero.service.ts', 'get-heroes', 'src/app/hero.service.ts (excerpt)')(format=".")
:marked :marked
You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server, You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server,
@ -268,7 +270,7 @@ code-example(format="nocode").
You have to change the implementation to *act on the !{_Promise} when it resolves*. You have to change the implementation to *act on the !{_Promise} when it resolves*.
When the !{_Promise} resolves successfully, you'll have heroes to display. When the !{_Promise} resolves successfully, you'll have heroes to display.
Pass the callback function as an argument to the !{_Promise}'s `then` method: Pass the callback function as an argument to the !{_Promise}'s `then()` method:
+makeExample('toh-4/ts/src/app/app.component.ts', 'get-heroes', 'src/app/app.component.ts (getHeroes - revised)')(format=".") +makeExample('toh-4/ts/src/app/app.component.ts', 'get-heroes', 'src/app/app.component.ts (getHeroes - revised)')(format=".")
.l-sub-section .l-sub-section
:marked :marked
@ -334,20 +336,20 @@ code-example(format="nocode").
## The road ahead ## The road ahead
The Tour of Heroes has become more reusable using shared components and services. The Tour of Heroes has become more reusable using shared components and services.
The next goal is to create a dashboard, add menu links that route between the views, and format data in a template. The next goal is to create a dashboard, add menu links that route between the views, and format data in a template.
As the app evolves, you'll learn how to design it to make it easier to grow and maintain. As the app evolves, you'll discover how to design it to make it easier to grow and maintain.
You learn about Angular component router and navigation among the views in the [next tutorial](toh-pt5.html) page. Read about the Angular component router and navigation among the views in the [next tutorial](toh-pt5.html) page.
.l-main-section .l-main-section
<a id="slow"></a> <a id="slow"></a>
:marked :marked
## Appendix: Take it slow ## Appendix: Take it slow
To simulate a slow connection, To simulate a slow connection,
import the `Hero` symbol and add the following `getHeroesSlowly` method to the `HeroService`. import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`.
+makeExample('toh-4/ts/src/app/hero.service.ts', 'get-heroes-slowly', 'app/hero.service.ts (getHeroesSlowly)')(format=".") +makeExample('toh-4/ts/src/app/hero.service.ts', 'get-heroes-slowly', 'app/hero.service.ts (getHeroesSlowly)')(format=".")
:marked :marked
Like `getHeroes`, it also returns a !{_Promise}. Like `getHeroes()`, it also returns a !{_Promise}.
But this !{_Promise} waits two seconds before resolving the !{_Promise} with mock heroes. But this !{_Promise} waits two seconds before resolving the !{_Promise} with mock heroes.
Back in the `AppComponent`, replace `heroService.getHeroes` with `heroService.getHeroesSlowly` Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()`
and see how the app behaves. and see how the app behaves.

View File

@ -12,8 +12,8 @@ block includes
* Add a *Dashboard* view. * Add a *Dashboard* view.
* Add the ability to navigate between the *Heroes* and *Dashboard* views. * Add the ability to navigate between the *Heroes* and *Dashboard* views.
* When users click a hero name in either view, they should navigate to a detail view of the selected hero. * When users click a hero name in either view, navigate to a detail view of the selected hero.
* When users click a *deep link* in an email, the detail view for a particular hero should open. * When users click a *deep link* in an email, open the detail view for a particular hero.
When youre done, users will be able to navigate the app like this: When youre done, users will be able to navigate the app like this:
@ -37,7 +37,6 @@ figure.image-display
:marked :marked
## Where you left off ## Where you left off
Before continuing with the Tour of Heroes, verify that you have the following structure. Before continuing with the Tour of Heroes, verify that you have the following structure.
If not, go back to the previous pages.
block intro-file-tree block intro-file-tree
.filetree .filetree
@ -215,14 +214,14 @@ block router-config-intro
:marked :marked
### Make the router available ### Make the router available
Add the initial route configuration, `RouterModule`, to the `AppModule` imports !{_array}. Import the `RouterModule` and add it to the `AppModule` imports !{_array}.
+makeExcerpt('src/app/app.module.2.ts (app routing)', '') +makeExcerpt('src/app/app.module.2.ts (app routing)', '')
.l-sub-section .l-sub-section
:marked :marked
The `forRoot` method is called because a configured router is provided at the app's root. The `forRoot()` method is called because a configured router is provided at the app's root.
The `forRoot` method supplies the Router service providers and directives needed for routing, and The `forRoot()` method supplies the Router service providers and directives needed for routing, and
performs the initial navigation based on the current browser URL. performs the initial navigation based on the current browser URL.
- var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes' - var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
@ -260,7 +259,7 @@ block routerLink
[Routing & Navigation](../guide/router.html#) page. [Routing & Navigation](../guide/router.html#) page.
:marked :marked
Refresh the browser. The app title and heroes link, but not the heroes list, display. Refresh the browser. The browser displays the app title and heroes link, but not the heroes list.
.l-sub-section .l-sub-section
:marked :marked
@ -269,7 +268,7 @@ block routerLink
Soon you'll add a route that matches the path `/`. Soon you'll add a route that matches the path `/`.
:marked :marked
Click the *Heroes* navigation link. The browser bar updates to `/heroes` Click the *Heroes* navigation link. The address bar updates to `/heroes`
and the list of heroes displays. and the list of heroes displays.
`AppComponent` now looks like this: `AppComponent` now looks like this:
@ -361,13 +360,13 @@ block templateUrl-path-resolution
:marked :marked
`*ngFor` is used again to iterate over a list of heroes and display their names. `*ngFor` is used again to iterate over a list of heroes and display their names.
The extra `<div>` elements will help with styling later in this page. The extra `<div>` elements will help with styling later.
### Sharing the *HeroService* ### Sharing the *HeroService*
To populate the component's `heroes` !{_array}, you can re-use the `HeroService`. To populate the component's `heroes` !{_array}, you can re-use the `HeroService`.
Earlier in the page, you removed the `HeroService` from the `providers` !{_array} of `HeroesComponent` Earlier, you removed the `HeroService` from the `providers` !{_array} of `HeroesComponent`
and added it to the `providers` !{_array} of `!{_AppModuleVsAppComp}`. and added it to the `providers` !{_array} of `!{_AppModuleVsAppComp}`.
That move created a singleton `HeroService` instance, available to all components of the app. That move created a singleton `HeroService` instance, available to all components of the app.
Angular injects `HeroService` and you can use it in the `DashboardComponent`. Angular injects `HeroService` and you can use it in the `DashboardComponent`.
@ -379,7 +378,7 @@ block templateUrl-path-resolution
+makeExcerpt('src/app/dashboard.component.ts','imports') +makeExcerpt('src/app/dashboard.component.ts','imports')
:marked :marked
Now implement the `DashboardComponent` class like this: Now create the `DashboardComponent` class like this:
+makeExcerpt('src/app/dashboard.component.ts (class)', 'class') +makeExcerpt('src/app/dashboard.component.ts (class)', 'class')
@ -474,7 +473,7 @@ code-example(format="nocode").
block route-params block route-params
:marked :marked
You'll no longer receive the hero in a parent component property binding. You'll no longer receive the hero in a parent component property binding.
The new `HeroDetailComponent` should take the `id` parameter from the `params` observable The new `HeroDetailComponent` should take the `id` parameter from the `params` Observable
in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`. in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`.
:marked :marked
@ -505,7 +504,7 @@ block route-params
block ngOnInit block ngOnInit
:marked :marked
Inside the `ngOnInit` lifecycle hook, use the `params` observable to Inside the `ngOnInit` lifecycle hook, use the `params` Observable to
extract the `id` parameter value from the `ActivatedRoute` service extract the `id` parameter value from the `ActivatedRoute` service
and use the `HeroService` to fetch the hero with that `id`. and use the `HeroService` to fetch the hero with that `id`.
@ -514,11 +513,11 @@ block ngOnInit
block extract-id block extract-id
:marked :marked
The `switchMap` operator maps the id in the observable route parameters The `switchMap` operator maps the `id` in the Observable route parameters
to a new `Observable`, the result of the `HeroService.getHero` method. to a new `Observable`, the result of the `HeroService.getHero()` method.
If a user re-navigates to this component while a getHero request is still processing, If a user re-navigates to this component while a `getHero` request is still processing,
`switchMap` cancels the old request and then calls `HeroService.getHero` again. `switchMap` cancels the old request and then calls `HeroService.getHero()` again.
- var _str2int = _docsFor == 'dart' ? '<code>int.parse</code> static method' : 'JavaScript (+) operator' - var _str2int = _docsFor == 'dart' ? '<code>int.parse</code> static method' : 'JavaScript (+) operator'
:marked :marked
@ -534,13 +533,13 @@ block extract-id
section of the [Routing & Navigation](../guide/router.html) page, section of the [Routing & Navigation](../guide/router.html) page,
the `Router` manages the observables it provides and localizes the `Router` manages the observables it provides and localizes
the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against
memory leaks, so you don't need to unsubscribe from the route params `Observable`. memory leaks, so you don't need to unsubscribe from the route `params` `Observable`.
:marked :marked
### Add *HeroService.getHero* ### Add *HeroService.getHero*
In the previous code snippet, `HeroService` doesn't have a `getHero` method. To fix this issue, In the previous code snippet, `HeroService` doesn't have a `getHero()` method. To fix this issue,
open `HeroService` and add a `getHero` method that filters the heroes list from `getHeroes` by `id`. open `HeroService` and add a `getHero()` method that filters the heroes list from `getHeroes` by `id`.
+makeExcerpt('src/app/hero.service.ts', 'getHero') +makeExcerpt('src/app/hero.service.ts', 'getHero')
@ -550,7 +549,7 @@ block extract-id
Users have several ways to navigate *to* the `HeroDetailComponent`. Users have several ways to navigate *to* the `HeroDetailComponent`.
To navigate somewhere else, users can click one of the two links in the `AppComponent` or click the browser's back button. To navigate somewhere else, users can click one of the two links in the `AppComponent` or click the browser's back button.
Now add a third option, a `goBack` method that navigates backward one step in the browser's history stack Now add a third option, a `goBack()` method that navigates backward one step in the browser's history stack
using the `Location` service you injected previously. using the `Location` service you injected previously.
+makeExcerpt('src/app/hero-detail.component.ts', 'goBack') +makeExcerpt('src/app/hero-detail.component.ts', 'goBack')
@ -633,8 +632,8 @@ block extract-id
establish key facts about the entire app for the Angular compiler. establish key facts about the entire app for the Angular compiler.
It's a good idea to refactor the routing configuration into its own class. It's a good idea to refactor the routing configuration into its own class.
The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders`. The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders`,
A class dedicated to routing should be a *routing module*. a class dedicated to routing should be a *routing module*.
For more information, see the [Milestone #2: The Routing Module](../guide/router.html#routing-module) For more information, see the [Milestone #2: The Routing Module](../guide/router.html#routing-module)
section of the [Routing & Navigation](../guide/router.html) page. section of the [Routing & Navigation](../guide/router.html) page.
@ -658,8 +657,8 @@ block extract-id
### Update *AppModule* ### Update *AppModule*
Delete the routing configuration from `AppModule` and import the `AppRoutingModule` Delete the routing configuration from `AppModule` and import the `AppRoutingModule`.
(*both* with an ES `import` statement *and* by adding it to the `NgModule.imports` list). Use an ES `import` statement *and* add it to the `NgModule.imports` list.
:marked :marked
Here is the revised `AppModule`, compared to its pre-refactor state: Here is the revised `AppModule`, compared to its pre-refactor state:
@ -696,9 +695,6 @@ block extract-id
Add the following HTML fragment at the bottom of the template where the `<hero-detail>` used to be: Add the following HTML fragment at the bottom of the template where the `<hero-detail>` used to be:
Add the following HTML fragment at the bottom of the template where the `<my-hero-detail>` used to be:
+makeExcerpt('src/app/heroes.component.html', 'mini-detail', 'src/app/heroes.component.ts') +makeExcerpt('src/app/heroes.component.html', 'mini-detail', 'src/app/heroes.component.ts')
@ -735,39 +731,54 @@ figure.image-display
Before making any more changes, migrate the template and styles to their own files. Before making any more changes, migrate the template and styles to their own files.
1. *Cut-and-paste* the template contents into a new <span ngio-ex>heroes.component.html</span> file. First, move the template contents from `heroes.component.ts`
1. *Cut-and-paste* the styles contents into a new <span ngio-ex>heroes.component.css</span> file. into a new <span ngio-ex>heroes.component.html</span> file.
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files. Don't copy the backticks. As for `heroes.component.ts`, you'll
come back to it in a minute. Next, move the
styles contents into a new <span ngio-ex>heroes.component.css</span> file.
The two new files should look like this:
+makeTabs(
`toh-5/ts/src/app/heroes.component.html, toh-5/ts/src/app/heroes.component.css`,
null,
`src/app/heroes.component.html, src/app/heroes.component.css`)
.l-sub-section
:marked :marked
The `styleUrls` property is !{_an} !{_array} of style file names (with paths). Now, back in the component metadata for `heroes.component.ts`,
You could list multiple style files from different locations if you needed them. delete `template` and `styles`, replacing them with
`templateUrl` and `styleUrls` respectively.
Set their properties to refer to the new files.
block heroes-component-cleanup block heroes-component-cleanup
//- Only relevant for Dart. //- Only relevant for Dart.
+makeExcerpt('src/app/heroes.component.ts (revised metadata)', 'metadata') +makeExcerpt('src/app/heroes.component.ts (revised metadata)', 'metadata')
.l-sub-section
:marked
The `styleUrls` property is !{_an} !{_array} of style file names (with paths).
You could list multiple style files from different locations if you needed them.
:marked :marked
### Update the _HeroesComponent_ class ### Update the _HeroesComponent_ class
The `HeroesComponent` navigates to the `HeroesDetailComponent` in response to a button click. The `HeroesComponent` navigates to the `HeroesDetailComponent` in response to a button click.
The button's click event is bound to a `gotoDetail` method that navigates _imperatively_ The button's click event is bound to a `gotoDetail()` method that navigates _imperatively_
by telling the router where to go. by telling the router where to go.
This approach requires the following changes to the component class: This approach requires the following changes to the component class:
1. Import the `router` from the Angular router library. 1. Import the `router` from the Angular router library.
1. Inject the `router` in the constructor (along with the `HeroService`). 1. Inject the `router` in the constructor, along with the `HeroService`.
1. Implement `gotoDetail` by calling the `router.navigate` method. 1. Implement `gotoDetail()` by calling the `router.navigate()` method.
+makeExcerpt('src/app/heroes.component.ts', 'gotoDetail') +makeExcerpt('src/app/heroes.component.ts', 'gotoDetail')
:marked :marked
Note that you're passing a two-element *link parameters !{_array}*&mdash;a Note that you're passing a two-element *link parameters !{_array}*&mdash;a
path and the route parameter&mdash;to path and the route parameter&mdash;to
the `router.navigate` method, just as you did in the `[routerLink]` binding the `router.navigate()` method, just as you did in the `[routerLink]` binding
back in the `DashboardComponent`. back in the `DashboardComponent`.
Here's the revised `HeroesComponent` class: Here's the revised `HeroesComponent` class:
@ -788,7 +799,7 @@ block heroes-component-cleanup
The dashboard heroes should display in a row of rectangles. The dashboard heroes should display in a row of rectangles.
You've received around 60 lines of CSS for this purpose, including some simple media queries for responsive design. You've received around 60 lines of CSS for this purpose, including some simple media queries for responsive design.
Adding the CSS to the component `styles` metadata As you now know, adding the CSS to the component `styles` metadata
would obscure the component logic. would obscure the component logic.
Instead, edit the CSS in a separate `*.css` file. Instead, edit the CSS in a separate `*.css` file.
@ -805,7 +816,7 @@ block heroes-component-cleanup
Add a <span ngio-ex>hero-detail.component.css</span> to the `!{_appDir}` Add a <span ngio-ex>hero-detail.component.css</span> to the `!{_appDir}`
folder and refer to that file inside folder and refer to that file inside
the `styleUrls` !{_array} as you did for `DashboardComponent`. the `styleUrls` !{_array} as you did for `DashboardComponent`.
Also, in hero-detail.component.ts, remove the `hero` property `@Input` !{_decorator} Also, in `hero-detail.component.ts`, remove the `hero` property `@Input` !{_decorator}
<span if-docs="ts">and its import</span>. <span if-docs="ts">and its import</span>.
Here's the content for the component CSS files. Here's the content for the component CSS files.

View File

@ -59,7 +59,7 @@ block http-library
block http-providers block http-providers
:marked :marked
The app will depend on the Angular `http` service, which itself depends on other supporting services. The app will depend on the Angular `http` service, which itself depends on other supporting services.
The `HttpModule` from `@angular/http` library holds providers for a complete set of HTTP services. The `HttpModule` from the `@angular/http` library holds providers for a complete set of HTTP services.
To allow access to these services from anywhere in the app, To allow access to these services from anywhere in the app,
add `HttpModule` to the `imports` list of the `AppModule`. add `HttpModule` to the `imports` list of the `AppModule`.
@ -67,7 +67,7 @@ block http-providers
+makeExample('src/app/app.module.ts', 'v1','src/app/app.module.ts (v1)') +makeExample('src/app/app.module.ts', 'v1','src/app/app.module.ts (v1)')
:marked :marked
Notice that you supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`. Notice that you also supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`.
.l-main-section .l-main-section
:marked :marked
@ -97,13 +97,13 @@ block backend
+makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '') +makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '')
:marked :marked
The `forRoot` configuration method takes an `InMemoryDataService` class The `forRoot()` configuration method takes an `InMemoryDataService` class
that primes the in-memory database. that primes the in-memory database.
Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content: Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content:
+makeExample('src/app/in-memory-data.service.ts', 'init')(format='.') +makeExample('src/app/in-memory-data.service.ts', 'init')(format='.')
:marked :marked
This file replaces the #[code #[+adjExPath('mock-heroes.ts')]], which is now safe to delete. This file replaces `mock-heroes.ts`, which is now safe to delete.
block dont-be-distracted-by-backend-subst block dont-be-distracted-by-backend-subst
.alert.is-helpful .alert.is-helpful
@ -125,7 +125,7 @@ block dont-be-distracted-by-backend-subst
+makeExcerpt('toh-4/ts/src/app/hero.service.ts (old getHeroes)', 'get-heroes') +makeExcerpt('toh-4/ts/src/app/hero.service.ts (old getHeroes)', 'get-heroes')
:marked :marked
This was implementated in anticipation of ultimately This was implemented in anticipation of ultimately
fetching heroes with an HTTP client, which must be an asynchronous operation. fetching heroes with an HTTP client, which must be an asynchronous operation.
Now convert `getHeroes()` to use HTTP. Now convert `getHeroes()` to use HTTP.
@ -171,7 +171,7 @@ block get-heroes-details
:marked :marked
### Extracting the data in the *then* callback ### Extracting the data in the *then* callback
In the *Promise*'s `then` callback, you call the `json` method of the HTTP `Response` to extract the In the *Promise*'s `then()` callback, you call the `json` method of the HTTP `Response` to extract the
data within the response. data within the response.
+makeExcerpt('src/app/hero.service.ts', 'to-data', '') +makeExcerpt('src/app/hero.service.ts', 'to-data', '')
@ -269,18 +269,19 @@ block get-heroes-details
### Add a hero service _update_ method ### Add a hero service _update_ method
The overall structure of the `update` method is similar to that of The overall structure of the `update` method is similar to that of
`getHeroes`, but it uses an HTTP *put* to persist server-side changes. `getHeroes`, but it uses an HTTP `put()` to persist server-side changes.
+makeExcerpt('src/app/hero.service.ts', 'update') +makeExcerpt('src/app/hero.service.ts', 'update')
:marked :marked
To identify which hero the server should update, the hero id is encoded in To identify which hero the server should update, the hero `id` is encoded in
the URL. The `put` body is the JSON string encoding of the hero, obtained by the URL. The `put()` body is the JSON string encoding of the hero, obtained by
calling `!{_JSON_stringify}`. The body content type calling `!{_JSON_stringify}`. The body content type
(`application/json`) is identified in the request header. (`application/json`) is identified in the request header.
Refresh the browser, change a hero name, save your change, and click the browser `Back` button. Changes should now persist. Refresh the browser, change a hero name, save your change,
and click the browser Back button. Changes should now persist.
.l-main-section .l-main-section
:marked :marked
@ -327,12 +328,12 @@ block get-heroes-details
+makeExcerpt('src/app/heroes.component.html', 'li-element') +makeExcerpt('src/app/heroes.component.html', 'li-element')
:marked :marked
In addition to calling the component's `delete` method, the delete-button In addition to calling the component's `delete()` method, the delete button's
click-handling code stops the propagation of the click event&mdash;you click handler code stops the propagation of the click event&mdash;you
don't want the `<li>` click handler to be triggered because doing so would don't want the `<li>` click handler to be triggered because doing so would
select the hero that the user will delete. select the hero that the user will delete.
The logic of the `delete` handler is a bit trickier: The logic of the `delete()` handler is a bit trickier:
+makeExcerpt('src/app/heroes.component.ts', 'delete') +makeExcerpt('src/app/heroes.component.ts', 'delete')
@ -348,9 +349,9 @@ block get-heroes-details
+makeExcerpt('src/app/heroes.component.css', 'additions') +makeExcerpt('src/app/heroes.component.css', 'additions')
:marked :marked
### Hero service `delete` method ### Hero service `delete()` method
Add the hero service's `delete` method, which uses the _delete_ HTTP method to remove the hero from the server: Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server:
+makeExcerpt('src/app/hero.service.ts', 'delete') +makeExcerpt('src/app/hero.service.ts', 'delete')
@ -369,26 +370,26 @@ block observables-section-intro
This section shows you how, when, and why to return the `Observable` directly. This section shows you how, when, and why to return the `Observable` directly.
### Background ### Background
An *observable* is a stream of events that you can process with array-like operators. An *Observable* is a stream of events that you can process with array-like operators.
Angular core has basic support for observables. Angular core has basic support for observables.
Developers augment that support with operators and extensions from the Developers augment that support with operators and extensions from the
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS">RxJS library</a>. <a href="http://reactivex.io/rxjs" target="_blank" title="RxJS">RxJS library</a>.
You'll see how shortly. You'll see how shortly.
Recall that the `HeroService` chained the `toPromise` operator to the `Observable` result of `http.get`. Recall that the `HeroService` chained the `toPromise` operator to the `Observable` result of `http.get()`.
That operator converted the `Observable` into a `Promise` and you passed that promise back to the caller. That operator converted the `Observable` into a `Promise` and you passed that promise back to the caller.
Converting to a promise is often a good choice. You typically ask `http.get` to fetch a single chunk of data. Converting to a Promise is often a good choice. You typically ask `http.get()` to fetch a single chunk of data.
When you receive the data, you're done. When you receive the data, you're done.
The calling component can easily consume a single result in the form of a promise. The calling component can easily consume a single result in the form of a Promise.
:marked :marked
But requests aren't always done only once. But requests aren't always done only once.
You may start one request, You may start one request,
cancel it, and make a different request before the server has responded to the first request. cancel it, and make a different request before the server has responded to the first request.
A *request-cancel-new-request* sequence is difficult to implement with *!{_Promise}s*, but A *request-cancel-new-request* sequence is difficult to implement with *!{Promise}s*, but
easy with *!{_Observable}s*. easy with *!{Observable}s*.
### Add the ability to search by name ### Add the ability to search by name
You're going to add a *hero search* feature to the Tour of Heroes. You're going to add a *hero search* feature to the Tour of Heroes.
@ -402,13 +403,13 @@ block observables-section-intro
The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one
in the `HeroService`, although the URL now has a query string. in the `HeroService`, although the URL now has a query string.
<span if-docs="ts">More importantly, you no longer call `toPromise`. <span if-docs="ts">More importantly, you no longer call `toPromise()`.
Instead you return the *observable* from the the `htttp.get`, Instead you return the *Observable* from the the `htttp.get()`,
after chaining it to another RxJS operator, <code>map</code>, after chaining it to another RxJS operator, <code>map()</code>,
to extract heroes from the response data. to extract heroes from the response data.
RxJS operator chaining makes response processing easy and readable. RxJS operator chaining makes response processing easy and readable.
See the [discuss below about operators](#rxjs-imports). See the [discussion below about operators](#rxjs-imports).
:marked :marked
### HeroSearchComponent ### HeroSearchComponent
@ -422,7 +423,8 @@ block observables-section-intro
Also, add styles for the new component. Also, add styles for the new component.
+makeExample('src/app/hero-search.component.css') +makeExample('src/app/hero-search.component.css')
:marked :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. As the user types in the search box, a *keyup* event binding calls the component's `search()`
method with the new search box value.
As expected, the `*ngFor` repeats hero objects from the component's `heroes` property. As expected, the `*ngFor` repeats hero objects from the component's `heroes` property.
@ -446,7 +448,7 @@ block search-criteria-intro
A `Subject` is a producer of an _observable_ event stream; A `Subject` is a producer of an _observable_ event stream;
`searchTerms` produces an `Observable` of strings, the filter criteria for the name search. `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`. Each call to `search()` puts a new string into this subject's _observable_ stream by calling `next()`.
:marked :marked
<a id="ngoninit"></a> <a id="ngoninit"></a>
@ -470,25 +472,25 @@ block observable-transformers
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds * `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
before passing along the latest string. You'll never make requests more frequently than 300ms. before passing along the latest string. You'll never make requests more frequently than 300ms.
* `distinctUntilChanged` ensures that a request is sent only if the filter text changed. * `distinctUntilChanged` ensures that a request is sent only if the filter text changed.
* `switchMap` calls the search service for each search term that makes it through `debounce` and `distinctUntilChanged`. * `switchMap()` calls the search service for each search term that makes it through `debounce` and `distinctUntilChanged`.
It cancels and discards previous search observables, returning only the latest search service observable. It cancels and discards previous search observables, returning only the latest search service observable.
.l-sub-section .l-sub-section
:marked :marked
With the [switchMap operator](http://www.learnrxjs.io/operators/transformation/switchmap.html) With the [switchMap operator](http://www.learnrxjs.io/operators/transformation/switchmap.html)
(formerly known as "flatMapLatest"), (formerly known as `flatMapLatest`),
every qualifying key event can trigger an `http` method call. every qualifying key event can trigger an `http()` method call.
Even with a 300ms pause between requests, you could have multiple HTTP requests in flight Even with a 300ms pause between requests, you could have multiple HTTP requests in flight
and they may not return in the order sent. and they may not return in the order sent.
`switchMap` preserves the original request order while returning `switchMap()` preserves the original request order while returning
only the observable from the most recent `http` method call. only the observable from the most recent `http` method call.
Results from prior calls are canceled and discarded. Results from prior calls are canceled and discarded.
If the search text is empty, the `http` method call is also short circuited If the search text is empty, the `http()` method call is also short circuited
and an observable containing an empty array is returned. and an observable containing an empty array is returned.
Note that until the service supports that feature, _canceling_ the `HeroSearchService` observable Note that until the service supports that feature, _canceling_ the `HeroSearchService` Observable
doesn't actually abort a pending HTTP request. doesn't actually abort a pending HTTP request.
For now, unwanted results are discarded. For now, unwanted results are discarded.
:marked :marked
@ -589,7 +591,7 @@ block filetree
You're at the end of your journey, and you've accomplished a lot. You're at the end of your journey, and you've accomplished a lot.
- You added the necessary dependencies to use HTTP in the app. - You added the necessary dependencies to use HTTP in the app.
- You refactored `HeroService` to load heroes from a web API. - You refactored `HeroService` to load heroes from a web API.
- You extended `HeroService` to support post, put, and delete methods. - You extended `HeroService` to support `post()`, `put()`, and `delete()` methods.
- You updated the components to allow adding, editing, and deleting of heroes. - You updated the components to allow adding, editing, and deleting of heroes.
- You configured an in-memory web API. - You configured an in-memory web API.
- You learned how to use !{_Observable}s. - You learned how to use !{_Observable}s.