docs(toh): copy edit and fixes in 4, 5, 6 and example (#3412)
This commit is contained in:
parent
56c8706262
commit
a705a0ca3c
|
@ -3,7 +3,7 @@
|
|||
// is solely for containing the transitory state of the 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 { ActivatedRoute, Params } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* #docregion */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
|
|
|
@ -92,7 +92,7 @@ code-example(language="sh" class="code-shell").
|
|||
|
||||
:marked
|
||||
### 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=".")
|
||||
|
||||
|
@ -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=".")
|
||||
:marked
|
||||
### 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:
|
||||
+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.
|
||||
a#child-component
|
||||
:marked
|
||||
### *getHeroes* in the *AppComponent*
|
||||
### *getHeroes()* in the *AppComponent*
|
||||
The service is in a `heroService` private variable.
|
||||
|
||||
You could call the service and get the data in one line.
|
||||
|
@ -198,12 +198,12 @@ a#child-component
|
|||
### The *ngOnInit* lifecycle hook
|
||||
`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,
|
||||
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.
|
||||
|
||||
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:
|
||||
at creation, after each change, and at its eventual destruction.
|
||||
|
||||
|
@ -221,7 +221,7 @@ code-example(format="nocode").
|
|||
export class AppComponent implements OnInit {}
|
||||
:marked
|
||||
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')
|
||||
:marked
|
||||
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
|
||||
## Async services and !{_Promise}s
|
||||
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=".")
|
||||
:marked
|
||||
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
|
||||
and you aren't able to block the UI during the wait.
|
||||
When using a remote server, users don't have to wait for the server to respond;
|
||||
additionally, you aren't able to block the UI during the wait.
|
||||
|
||||
: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}
|
||||
|
||||
|
@ -253,7 +255,7 @@ code-example(format="nocode").
|
|||
[Exploring ES6](http://http://exploringjs.com/es6.html).
|
||||
|
||||
: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=".")
|
||||
:marked
|
||||
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*.
|
||||
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=".")
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -334,20 +336,20 @@ code-example(format="nocode").
|
|||
## The road ahead
|
||||
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.
|
||||
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
|
||||
<a id="slow"></a>
|
||||
:marked
|
||||
## Appendix: Take it slow
|
||||
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=".")
|
||||
: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.
|
||||
|
||||
Back in the `AppComponent`, replace `heroService.getHeroes` with `heroService.getHeroesSlowly`
|
||||
Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()`
|
||||
and see how the app behaves.
|
||||
|
|
|
@ -12,8 +12,8 @@ block includes
|
|||
|
||||
* Add a *Dashboard* view.
|
||||
* 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 *deep link* in an email, the detail view for a particular hero should open.
|
||||
* 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, open the detail view for a particular hero.
|
||||
|
||||
When you’re done, users will be able to navigate the app like this:
|
||||
|
||||
|
@ -37,7 +37,6 @@ figure.image-display
|
|||
:marked
|
||||
## Where you left off
|
||||
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
|
||||
.filetree
|
||||
|
@ -215,14 +214,14 @@ block router-config-intro
|
|||
:marked
|
||||
### 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)', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
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 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
|
||||
performs the initial navigation based on the current browser URL.
|
||||
|
||||
- var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
|
||||
|
@ -260,7 +259,7 @@ block routerLink
|
|||
[Routing & Navigation](../guide/router.html#) page.
|
||||
|
||||
: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
|
||||
:marked
|
||||
|
@ -269,7 +268,7 @@ block routerLink
|
|||
Soon you'll add a route that matches the path `/`.
|
||||
|
||||
: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.
|
||||
|
||||
`AppComponent` now looks like this:
|
||||
|
@ -361,13 +360,13 @@ block templateUrl-path-resolution
|
|||
|
||||
:marked
|
||||
`*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*
|
||||
|
||||
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}`.
|
||||
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`.
|
||||
|
@ -379,7 +378,7 @@ block templateUrl-path-resolution
|
|||
+makeExcerpt('src/app/dashboard.component.ts','imports')
|
||||
|
||||
:marked
|
||||
Now implement the `DashboardComponent` class like this:
|
||||
Now create the `DashboardComponent` class like this:
|
||||
|
||||
+makeExcerpt('src/app/dashboard.component.ts (class)', 'class')
|
||||
|
||||
|
@ -474,7 +473,7 @@ code-example(format="nocode").
|
|||
block route-params
|
||||
:marked
|
||||
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`.
|
||||
|
||||
:marked
|
||||
|
@ -505,7 +504,7 @@ block route-params
|
|||
|
||||
block ngOnInit
|
||||
: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
|
||||
and use the `HeroService` to fetch the hero with that `id`.
|
||||
|
||||
|
@ -514,11 +513,11 @@ block ngOnInit
|
|||
|
||||
block extract-id
|
||||
:marked
|
||||
The `switchMap` operator maps the id in the observable route parameters
|
||||
to a new `Observable`, the result of the `HeroService.getHero` method.
|
||||
The `switchMap` operator maps the `id` in the Observable route parameters
|
||||
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,
|
||||
`switchMap` cancels the old request and then calls `HeroService.getHero` again.
|
||||
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.
|
||||
|
||||
- var _str2int = _docsFor == 'dart' ? '<code>int.parse</code> static method' : 'JavaScript (+) operator'
|
||||
:marked
|
||||
|
@ -534,13 +533,13 @@ block extract-id
|
|||
section of the [Routing & Navigation](../guide/router.html) page,
|
||||
the `Router` manages the observables it provides and localizes
|
||||
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
|
||||
### Add *HeroService.getHero*
|
||||
|
||||
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`.
|
||||
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`.
|
||||
|
||||
+makeExcerpt('src/app/hero.service.ts', 'getHero')
|
||||
|
||||
|
@ -550,7 +549,7 @@ block extract-id
|
|||
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.
|
||||
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.
|
||||
|
||||
+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.
|
||||
|
||||
It's a good idea to refactor the routing configuration into its own class.
|
||||
The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders`.
|
||||
A class dedicated to routing should be a *routing module*.
|
||||
The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders`,
|
||||
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)
|
||||
section of the [Routing & Navigation](../guide/router.html) page.
|
||||
|
||||
|
@ -658,8 +657,8 @@ block extract-id
|
|||
|
||||
### Update *AppModule*
|
||||
|
||||
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).
|
||||
Delete the routing configuration from `AppModule` and import the `AppRoutingModule`.
|
||||
Use an ES `import` statement *and* add it to the `NgModule.imports` list.
|
||||
|
||||
:marked
|
||||
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 `<my-hero-detail>` used to be:
|
||||
|
||||
+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.
|
||||
|
||||
1. *Cut-and-paste* the template contents into a new <span ngio-ex>heroes.component.html</span> file.
|
||||
1. *Cut-and-paste* the styles contents into a new <span ngio-ex>heroes.component.css</span> file.
|
||||
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
|
||||
First, move the template contents from `heroes.component.ts`
|
||||
into a new <span ngio-ex>heroes.component.html</span> file.
|
||||
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
|
||||
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.
|
||||
Now, back in the component metadata for `heroes.component.ts`,
|
||||
delete `template` and `styles`, replacing them with
|
||||
`templateUrl` and `styleUrls` respectively.
|
||||
Set their properties to refer to the new files.
|
||||
|
||||
block heroes-component-cleanup
|
||||
//- Only relevant for Dart.
|
||||
|
||||
+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
|
||||
### Update the _HeroesComponent_ class
|
||||
|
||||
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.
|
||||
|
||||
This approach requires the following changes to the component class:
|
||||
|
||||
1. Import the `router` from the Angular router library.
|
||||
1. Inject the `router` in the constructor (along with the `HeroService`).
|
||||
1. Implement `gotoDetail` by calling the `router.navigate` method.
|
||||
1. Inject the `router` in the constructor, along with the `HeroService`.
|
||||
1. Implement `gotoDetail()` by calling the `router.navigate()` method.
|
||||
|
||||
+makeExcerpt('src/app/heroes.component.ts', 'gotoDetail')
|
||||
|
||||
:marked
|
||||
Note that you're passing a two-element *link parameters !{_array}*—a
|
||||
path and the route parameter—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`.
|
||||
Here's the revised `HeroesComponent` class:
|
||||
|
||||
|
@ -788,7 +799,7 @@ block heroes-component-cleanup
|
|||
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.
|
||||
|
||||
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.
|
||||
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}`
|
||||
folder and refer to that file inside
|
||||
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>.
|
||||
|
||||
Here's the content for the component CSS files.
|
||||
|
|
|
@ -59,7 +59,7 @@ block http-library
|
|||
block http-providers
|
||||
:marked
|
||||
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,
|
||||
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)')
|
||||
|
||||
: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
|
||||
:marked
|
||||
|
@ -97,13 +97,13 @@ block backend
|
|||
+makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '')
|
||||
|
||||
:marked
|
||||
The `forRoot` configuration method takes an `InMemoryDataService` class
|
||||
The `forRoot()` configuration method takes an `InMemoryDataService` class
|
||||
that primes the in-memory database.
|
||||
Add the file `in-memory-data.service.ts` in `!{_appDir}` with the following content:
|
||||
|
||||
+makeExample('src/app/in-memory-data.service.ts', 'init')(format='.')
|
||||
: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
|
||||
.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')
|
||||
|
||||
: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.
|
||||
|
||||
Now convert `getHeroes()` to use HTTP.
|
||||
|
@ -171,7 +171,7 @@ block get-heroes-details
|
|||
:marked
|
||||
### 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.
|
||||
|
||||
+makeExcerpt('src/app/hero.service.ts', 'to-data', '')
|
||||
|
@ -269,18 +269,19 @@ block get-heroes-details
|
|||
### Add a hero service _update_ method
|
||||
|
||||
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')
|
||||
|
||||
:marked
|
||||
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
|
||||
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
|
||||
calling `!{_JSON_stringify}`. The body content type
|
||||
(`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
|
||||
:marked
|
||||
|
@ -327,12 +328,12 @@ block get-heroes-details
|
|||
+makeExcerpt('src/app/heroes.component.html', 'li-element')
|
||||
|
||||
:marked
|
||||
In addition to calling the component's `delete` method, the delete-button
|
||||
click-handling code stops the propagation of the click event—you
|
||||
In addition to calling the component's `delete()` method, the delete button's
|
||||
click handler code stops the propagation of the click event—you
|
||||
don't want the `<li>` click handler to be triggered because doing so would
|
||||
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')
|
||||
|
||||
|
@ -348,9 +349,9 @@ block get-heroes-details
|
|||
+makeExcerpt('src/app/heroes.component.css', 'additions')
|
||||
|
||||
: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')
|
||||
|
||||
|
@ -369,26 +370,26 @@ block observables-section-intro
|
|||
This section shows you how, when, and why to return the `Observable` directly.
|
||||
|
||||
### 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.
|
||||
Developers augment that support with operators and extensions from the
|
||||
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS">RxJS library</a>.
|
||||
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.
|
||||
|
||||
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.
|
||||
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
|
||||
But requests aren't always done only once.
|
||||
You may start one 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
|
||||
easy with *!{_Observable}s*.
|
||||
A *request-cancel-new-request* sequence is difficult to implement with *!{Promise}s*, but
|
||||
easy with *!{Observable}s*.
|
||||
|
||||
### Add the ability to search by name
|
||||
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
|
||||
in the `HeroService`, although the URL now has a query string.
|
||||
|
||||
<span if-docs="ts">More importantly, you no longer call `toPromise`.
|
||||
Instead you return the *observable* from the the `htttp.get`,
|
||||
after chaining it to another RxJS operator, <code>map</code>,
|
||||
<span if-docs="ts">More importantly, you no longer call `toPromise()`.
|
||||
Instead you return the *Observable* from the the `htttp.get()`,
|
||||
after chaining it to another RxJS operator, <code>map()</code>,
|
||||
to extract heroes from the response data.
|
||||
|
||||
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
|
||||
### HeroSearchComponent
|
||||
|
@ -422,7 +423,8 @@ block observables-section-intro
|
|||
Also, add styles for the new component.
|
||||
+makeExample('src/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.
|
||||
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.
|
||||
|
||||
|
@ -446,7 +448,7 @@ block search-criteria-intro
|
|||
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`.
|
||||
Each call to `search()` puts a new string into this subject's _observable_ stream by calling `next()`.
|
||||
|
||||
:marked
|
||||
<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
|
||||
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.
|
||||
* `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.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
With the [switchMap operator](http://www.learnrxjs.io/operators/transformation/switchmap.html)
|
||||
(formerly known as "flatMapLatest"),
|
||||
every qualifying key event can trigger an `http` method call.
|
||||
(formerly known as `flatMapLatest`),
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
For now, unwanted results are discarded.
|
||||
:marked
|
||||
|
@ -589,7 +591,7 @@ block filetree
|
|||
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 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 configured an in-memory web API.
|
||||
- You learned how to use !{_Observable}s.
|
||||
|
|
Loading…
Reference in New Issue