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.
|
// 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';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* #docregion */
|
||||||
.selected {
|
.selected {
|
||||||
background-color: #CFD8DC !important;
|
background-color: #CFD8DC !important;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 you’re done, users will be able to navigate the app like this:
|
When you’re 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}*—a
|
Note that you're passing a two-element *link parameters !{_array}*—a
|
||||||
path and the route parameter—to
|
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`.
|
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.
|
||||||
|
|
|
@ -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—you
|
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
|
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.
|
||||||
|
|
Loading…
Reference in New Issue