docs(aio): remove excess h1s from guides
Only one h1 is allowed per document. (Also took the opportunity to remove unnecessary blank lines from these docs, and a bit of general tidying.) Closes #16193
This commit is contained in:
parent
bcbee13e26
commit
56ccd5e6a1
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,4 @@
|
|||
@title
|
||||
HTTP
|
||||
|
||||
@intro
|
||||
Convert the service and components to use Angular's HTTP service.
|
||||
|
||||
@description
|
||||
|
||||
|
||||
# HTTP
|
||||
|
||||
In this page, you'll make the following improvements.
|
||||
|
||||
|
@ -18,39 +10,25 @@ You'll teach the app to make corresponding HTTP calls to a remote server's web A
|
|||
|
||||
When you're done with this page, the app should look like this <live-example></live-example>.
|
||||
|
||||
|
||||
|
||||
## Where you left off
|
||||
|
||||
In the [previous page](tutorial/toh-pt5), you learned to navigate between the dashboard and the fixed heroes list,
|
||||
editing a selected hero along the way.
|
||||
That's the starting point for this page.
|
||||
|
||||
|
||||
## Keep the app transpiling and running
|
||||
Enter the following command in the terminal window:
|
||||
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm start
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
|
||||
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
|
||||
|
||||
|
||||
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
|
||||
|
||||
|
||||
|
||||
<h1>
|
||||
Providing HTTP Services
|
||||
</h1>
|
||||
|
||||
|
||||
## Providing HTTP Services
|
||||
|
||||
The `HttpModule` is not a core Angular module.
|
||||
`HttpModule` is Angular's optional approach to web access. It exists as a separate add-on module called `@angular/http`
|
||||
|
@ -58,28 +36,20 @@ and is shipped in a separate script file as part of the Angular npm package.
|
|||
|
||||
You're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when you need it.
|
||||
|
||||
|
||||
## Register for HTTP services
|
||||
|
||||
|
||||
The app will depend on the Angular `http` service, which itself depends on other supporting 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`.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="v1" title="src/app/app.module.ts (v1)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="v1" title="src/app/app.module.ts (v1)"></code-example>
|
||||
|
||||
Notice that you also supply `HttpModule` as part of the *imports* array in root NgModule `AppModule`.
|
||||
|
||||
|
||||
|
||||
## Simulate the web API
|
||||
|
||||
We recommend registering app-wide services in the root
|
||||
`AppModule` *providers*.
|
||||
|
||||
|
@ -89,93 +59,59 @@ a mock service, the *in-memory web API*.
|
|||
|
||||
Update <code>src/app/app.module.ts</code> with this version, which uses the mock service:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="v2" title="src/app/app.module.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="v2" title="src/app/app.module.ts (v2)"></code-example>
|
||||
|
||||
Rather than require a real API server, this example simulates communication with the remote server by adding the
|
||||
<a href="https://github.com/angular/in-memory-web-api" target="_blank" title="In-memory Web API">InMemoryWebApiModule</a>
|
||||
<a href="https://github.com/angular/in-memory-web-api" title="In-memory Web API">InMemoryWebApiModule</a>
|
||||
to the module `imports`, effectively replacing the `Http` client's XHR backend service with an in-memory alternative.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="in-mem-web-api">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="in-mem-web-api"></code-example>
|
||||
|
||||
The `forRoot()` configuration method takes an `InMemoryDataService` class
|
||||
that primes the in-memory database.
|
||||
Add the file `in-memory-data.service.ts` in `app` with the following content:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" title="src/app/in-memory-data.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" title="src/app/in-memory-data.service.ts" linenums="false"></code-example>
|
||||
|
||||
This file replaces `mock-heroes.ts`, which is now safe to delete.
|
||||
Added hero "Zero" to confirm that the data service can handle a hero with `id==0`.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
|
||||
Don't worry about the details of this backend substitution; you can
|
||||
skip it when you have a real web API server.
|
||||
|
||||
|
||||
The in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
|
||||
Don't worry about the details of this backend substitution; you can
|
||||
skip it when you have a real web API server.
|
||||
|
||||
Read more about the in-memory web API in the
|
||||
[Appendix: Tour of Heroes in-memory web api](guide/http#in-mem-web-api)
|
||||
section of the [HTTP Client](guide/http#in-mem-web-api) page.
|
||||
|
||||
Read more about the in-memory web API in the
|
||||
[Appendix: Tour of Heroes in-memory web api](guide/http#in-mem-web-api)
|
||||
section of the [HTTP Client](guide/http#in-mem-web-api) page.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
## Heroes and HTTP
|
||||
|
||||
In the current `HeroService` implementation, a Promise resolved with mock heroes is returned.
|
||||
|
||||
|
||||
<code-example path="toh-pt4/src/app/hero.service.ts" region="get-heroes" title="src/app/hero.service.ts (old getHeroes)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt4/src/app/hero.service.ts" region="get-heroes" title="src/app/hero.service.ts (old getHeroes)"></code-example>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="getHeroes" title="src/app/hero.service.ts (updated getHeroes and new class members)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="getHeroes" title="src/app/hero.service.ts (updated getHeroes and new class members)"></code-example>
|
||||
|
||||
Update the import statements as follows:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="imports" title="src/app/hero.service.ts (updated imports)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="imports" title="src/app/hero.service.ts (updated imports)"></code-example>
|
||||
|
||||
Refresh the browser. The hero data should successfully load from the
|
||||
mock server.
|
||||
|
||||
<h3 id="http-promise">HTTP Promise</h3>
|
||||
{@a http-promise}
|
||||
|
||||
### HTTP Promise
|
||||
|
||||
The Angular `http.get` returns an RxJS `Observable`.
|
||||
*Observables* are a powerful way to manage asynchronous data flows.
|
||||
|
@ -183,12 +119,7 @@ You'll read about [Observables](tutorial/toh-pt6#observables) later in this page
|
|||
|
||||
For now, you've converted the `Observable` to a `Promise` using the `toPromise` operator.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="to-promise">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="to-promise"></code-example>
|
||||
|
||||
The Angular `Observable` doesn't have a `toPromise` operator out of the box.
|
||||
|
||||
|
@ -196,35 +127,22 @@ There are many operators like `toPromise` that extend `Observable` with useful c
|
|||
To use those capabilities, you have to add the operators themselves.
|
||||
That's as easy as importing them from the RxJS library like this:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="rxjs">
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="rxjs"></code-example>
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
You'll add more operators, and learn why you must do so, [later in this tutorial](tutorial/toh-pt6#rxjs-imports).
|
||||
|
||||
You'll add more operators, and learn why you must do so, [later in this tutorial](tutorial/toh-pt6#rxjs-imports).
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
### 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
|
||||
data within the response.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="to-data">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="to-data"></code-example>
|
||||
|
||||
The response JSON has a single `data` property, which
|
||||
holds the array of heroes that the caller wants.
|
||||
|
@ -233,17 +151,13 @@ So you grab that array and return it as the resolved Promise value.
|
|||
|
||||
<div class="alert is-important">
|
||||
|
||||
|
||||
|
||||
Note the shape of the data that the server returns.
|
||||
This particular in-memory web API example returns an object with a `data` property.
|
||||
Your API might return something else. Adjust the code to match your web API.
|
||||
|
||||
Note the shape of the data that the server returns.
|
||||
This particular in-memory web API example returns an object with a `data` property.
|
||||
Your API might return something else. Adjust the code to match your web API.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
The caller is unaware that you fetched the heroes from the (mock) server.
|
||||
It receives a Promise of *heroes* just as it did before.
|
||||
|
||||
|
@ -251,22 +165,12 @@ It receives a Promise of *heroes* just as it did before.
|
|||
|
||||
At the end of `getHeroes()`, you `catch` server failures and pass them to an error handler.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="catch">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="catch"></code-example>
|
||||
|
||||
This is a critical step.
|
||||
You must anticipate HTTP failures, as they happen frequently for reasons beyond your control.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="handleError">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="handleError"></code-example>
|
||||
|
||||
This demo service logs the error to the console; in real life,
|
||||
you would handle the error in code. For a demo, this works.
|
||||
|
@ -274,7 +178,6 @@ you would handle the error in code. For a demo, this works.
|
|||
The code also includes an error to
|
||||
the caller in a rejected promise, so that the caller can display a proper error message to the user.
|
||||
|
||||
|
||||
### Get hero by id
|
||||
|
||||
When the `HeroDetailComponent` asks the `HeroService` to fetch a hero,
|
||||
|
@ -285,11 +188,7 @@ Most web APIs support a _get-by-id_ request in the form `api/hero/:id` (such as
|
|||
|
||||
Update the `HeroService.getHero()` method to make a _get-by-id_ request:
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="getHero" title="src/app/hero.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="getHero" title="src/app/hero.service.ts"></code-example>
|
||||
|
||||
This request is almost the same as `getHeroes()`.
|
||||
The hero id in the URL identifies which hero the server should update.
|
||||
|
@ -305,8 +204,6 @@ You won't have to update any of the components that call them.
|
|||
|
||||
Now it's time to add the ability to create and delete heroes.
|
||||
|
||||
|
||||
|
||||
## Updating hero details
|
||||
|
||||
Try editing a hero's name in the hero detail view.
|
||||
|
@ -324,35 +221,19 @@ the server.
|
|||
At the end of the hero detail template, add a save button with a `click` event
|
||||
binding that invokes a new component method named `save()`.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-detail.component.html" region="save" title="src/app/hero-detail.component.html (save)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-detail.component.html" region="save" title="src/app/hero-detail.component.html (save)"></code-example>
|
||||
|
||||
Add the following `save()` method, which persists hero name changes using the hero service
|
||||
`update()` method and then navigates back to the previous view.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-detail.component.ts" region="save" title="src/app/hero-detail.component.ts (save)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-detail.component.ts" region="save" title="src/app/hero-detail.component.ts (save)"></code-example>
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="update" title="src/app/hero.service.ts (update)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="update" title="src/app/hero.service.ts (update)"></code-example>
|
||||
|
||||
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
|
||||
|
@ -362,8 +243,6 @@ calling `JSON.stringify`. The body content type
|
|||
Refresh the browser, change a hero name, save your change,
|
||||
and click the browser Back button. Changes should now persist.
|
||||
|
||||
|
||||
|
||||
## Add the ability to add heroes
|
||||
|
||||
To add a hero, the app needs the hero's name. You can use an `input`
|
||||
|
@ -372,38 +251,22 @@ element paired with an add button.
|
|||
Insert the following into the heroes component HTML, just after
|
||||
the heading:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="add" title="src/app/heroes.component.html (add)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="add" title="src/app/heroes.component.html (add)"></code-example>
|
||||
|
||||
In response to a click event, call the component's click handler and then
|
||||
clear the input field so that it's ready for another name.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.ts" region="add" title="src/app/heroes.component.ts (add)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.ts" region="add" title="src/app/heroes.component.ts (add)"></code-example>
|
||||
|
||||
When the given name is non-blank, the handler delegates creation of the
|
||||
named hero to the hero service, and then adds the new hero to the array.
|
||||
|
||||
Implement the `create()` method in the `HeroService` class.
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="create" title="src/app/hero.service.ts (create)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="create" title="src/app/hero.service.ts (create)"></code-example>
|
||||
|
||||
Refresh the browser and create some heroes.
|
||||
|
||||
|
||||
|
||||
## Add the ability to delete a hero
|
||||
|
||||
Each hero in the heroes view should have a delete button.
|
||||
|
@ -411,21 +274,11 @@ Each hero in the heroes view should have a delete button.
|
|||
Add the following button element to the heroes component HTML, after the hero
|
||||
name in the repeated `<li>` element.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="delete">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="delete"></code-example>
|
||||
|
||||
The `<li>` element should now look like this:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="li-element" title="src/app/heroes.component.html (li-element)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.html" region="li-element" title="src/app/heroes.component.html (li-element)"></code-example>
|
||||
|
||||
In addition to calling the component's `delete()` method, the delete button's
|
||||
click handler code stops the propagation of the click event—you
|
||||
|
@ -434,57 +287,34 @@ select the hero that the user will delete.
|
|||
|
||||
The logic of the `delete()` handler is a bit trickier:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.ts" region="delete" title="src/app/heroes.component.ts (delete)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.ts" region="delete" title="src/app/heroes.component.ts (delete)"></code-example>
|
||||
|
||||
Of course you delegate hero deletion to the hero service, but the component
|
||||
is still responsible for updating the display: it removes the deleted hero
|
||||
from the array and resets the selected hero, if necessary.
|
||||
|
||||
|
||||
To place the delete button at the far right of the hero entry,
|
||||
add this CSS:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.css" region="additions" title="src/app/heroes.component.css (additions)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/heroes.component.css" region="additions" title="src/app/heroes.component.css (additions)"></code-example>
|
||||
|
||||
### Hero service _delete()_ method
|
||||
|
||||
Add the hero service's `delete()` method, which uses the `delete()` HTTP method to remove the hero from the server:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="delete" title="src/app/hero.service.ts (delete)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero.service.ts" region="delete" title="src/app/hero.service.ts (delete)"></code-example>
|
||||
|
||||
Refresh the browser and try the new delete functionality.
|
||||
|
||||
|
||||
<div id='observables'>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
## Observables
|
||||
|
||||
|
||||
Each `Http` service method returns an `Observable` of HTTP `Response` objects.
|
||||
|
||||
The `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
|
||||
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.
|
||||
|
||||
Angular core has basic support for observables.
|
||||
|
@ -499,7 +329,6 @@ Converting to a Promise is often a good choice. You typically ask `http.get()` t
|
|||
When you receive the data, you're done.
|
||||
The calling component can easily consume a single result in the form of a Promise.
|
||||
|
||||
|
||||
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.
|
||||
|
@ -508,17 +337,13 @@ A *request-cancel-new-request* sequence is difficult to implement with `Promise`
|
|||
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.
|
||||
As the user types a name into a search box, you'll make repeated HTTP requests for heroes filtered by that name.
|
||||
|
||||
Start by creating `HeroSearchService` that sends search queries to the server's web API.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.service.ts" title="src/app/hero-search.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.service.ts" title="src/app/hero-search.service.ts"></code-example>
|
||||
|
||||
The `http.get()` call in `HeroSearchService` is similar to the one
|
||||
in the `HeroService`, although the URL now has a query string.
|
||||
|
@ -530,27 +355,17 @@ to extract heroes from the response data.
|
|||
RxJS operator chaining makes response processing easy and readable.
|
||||
See the [discussion below about operators](tutorial/toh-pt6#rxjs-imports).</span>
|
||||
|
||||
|
||||
### HeroSearchComponent
|
||||
|
||||
Create a `HeroSearchComponent` that calls the new `HeroSearchService`.
|
||||
|
||||
The component template is simple—just a text box and a list of matching search results.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.html" title="src/app/hero-search.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.html" title="src/app/hero-search.component.html"></code-example>
|
||||
|
||||
Also, add styles for the new component.
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.css" title="src/app/hero-search.component.css">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.css" title="src/app/hero-search.component.css"></code-example>
|
||||
|
||||
As the user types in the search box, a *keyup* event binding calls the component's `search()`
|
||||
method with the new search box value.
|
||||
|
@ -563,50 +378,32 @@ The `async` pipe subscribes to the `Observable` and produces the array of heroes
|
|||
|
||||
Create the `HeroSearchComponent` class and metadata.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" title="src/app/hero-search.component.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" title="src/app/hero-search.component.ts"></code-example>
|
||||
|
||||
#### Search terms
|
||||
|
||||
Focus on the `searchTerms`:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="searchTerms">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="searchTerms"></code-example>
|
||||
|
||||
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()`.
|
||||
|
||||
|
||||
{@a ngoninit}
|
||||
|
||||
|
||||
#### Initialize the *heroes* property (*ngOnInit*)
|
||||
|
||||
A `Subject` is also an `Observable`.
|
||||
You can turn the stream
|
||||
of search terms into a stream of `Hero` arrays and assign the result to the `heroes` property.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="search">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="search"></code-example>
|
||||
|
||||
Passing every user keystroke directly to the `HeroSearchService` would create an excessive amount of HTTP requests,
|
||||
taxing server resources and burning through the cellular network data plan.
|
||||
|
||||
|
||||
Instead, you can chain `Observable` operators that reduce the request flow to the string `Observable`.
|
||||
You'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
|
||||
|
||||
|
@ -619,37 +416,31 @@ It cancels and discards previous search observables, returning only the latest s
|
|||
|
||||
<div class="l-sub-section">
|
||||
|
||||
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.
|
||||
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 only the observable from the most recent `http` method call.
|
||||
Results from prior calls are canceled and discarded.
|
||||
|
||||
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.
|
||||
Even with a 300ms pause between requests, you could have multiple HTTP requests in flight
|
||||
and they may not return in the order sent.
|
||||
If the search text is empty, the `http()` method call is also short circuited
|
||||
and an observable containing an empty array is returned.
|
||||
|
||||
`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
|
||||
and an observable containing an empty array is returned.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
* `catch` intercepts a failed observable.
|
||||
The simple example prints the error to the console; a real life app would do better.
|
||||
Then to clear the search result, you return an observable containing an empty array.
|
||||
|
||||
|
||||
{@a rxjs-imports}
|
||||
|
||||
|
||||
### Import RxJS operators
|
||||
|
||||
Most RxJS operators are not included in Angular's base `Observable` implementation.
|
||||
|
@ -658,12 +449,7 @@ The base implementation includes only what Angular itself requires.
|
|||
When you need more RxJS features, extend `Observable` by *importing* the libraries in which they are defined.
|
||||
Here are all the RxJS imports that _this_ component needs:
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" title="src/app/hero-search.component.ts (rxjs imports)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search.component.ts" region="rxjs-imports" title="src/app/hero-search.component.ts (rxjs imports)" linenums="false"></code-example>
|
||||
|
||||
The `import 'rxjs/add/...'` syntax may be unfamiliar.
|
||||
It's missing the usual list of symbols between the braces: `{...}`.
|
||||
|
@ -672,185 +458,70 @@ You don't need the operator symbols themselves.
|
|||
In each case, the mere act of importing the library
|
||||
loads and executes the library's script file which, in turn, adds the operator to the `Observable` class.
|
||||
|
||||
|
||||
### Add the search component to the dashboard
|
||||
|
||||
Add the hero search HTML element to the bottom of the `DashboardComponent` template.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/dashboard.component.html" title="src/app/dashboard.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/dashboard.component.html" title="src/app/dashboard.component.html" linenums="false"></code-example>
|
||||
|
||||
Finally, import `HeroSearchComponent` from
|
||||
<code>hero-search.component.ts</code>
|
||||
and add it to the `declarations` array.
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="search" title="src/app/app.module.ts (search)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="toh-pt6/src/app/app.module.ts" region="search" title="src/app/app.module.ts (search)"></code-example>
|
||||
|
||||
Run the app again. In the Dashboard, enter some text in the search box.
|
||||
If you enter characters that match any existing hero names, you'll see something like this.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
|
||||
## App structure and code
|
||||
|
||||
Review the sample source code in the <live-example></live-example> for this page.
|
||||
Verify that you have the following structure:
|
||||
|
||||
|
||||
<div class='filetree'>
|
||||
|
||||
<div class='file'>
|
||||
angular-tour-of-heroes
|
||||
</div>
|
||||
|
||||
<div class='file'>angular-tour-of-heroes</div>
|
||||
<div class='children'>
|
||||
|
||||
<div class='file'>
|
||||
src
|
||||
</div>
|
||||
|
||||
<div class='file'>src</div>
|
||||
<div class='children'>
|
||||
|
||||
<div class='file'>
|
||||
app
|
||||
</div>
|
||||
|
||||
<div class='file'>app</div>
|
||||
<div class='children'>
|
||||
|
||||
<div class='file'>
|
||||
app.component.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
app.component.css
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
app.module.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
app-routing.module.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
dashboard.component.css
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
dashboard.component.html
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
dashboard.component.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-detail.component.css
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-detail.component.html
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-detail.component.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-search.component.html (new)
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-search.component.css (new)
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-search.component.ts (new)
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero-search.service.ts (new)
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
hero.service.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
heroes.component.css
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
heroes.component.html
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
heroes.component.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
in-memory-data.service.ts (new)
|
||||
</div>
|
||||
|
||||
<div class='file'>app.component.ts</div>
|
||||
<div class='file'>app.component.css</div>
|
||||
<div class='file'>app.module.ts</div>
|
||||
<div class='file'>app-routing.module.ts</div>
|
||||
<div class='file'>dashboard.component.css</div>
|
||||
<div class='file'>dashboard.component.html</div>
|
||||
<div class='file'>dashboard.component.ts</div>
|
||||
<div class='file'>hero.ts</div>
|
||||
<div class='file'>hero-detail.component.css</div>
|
||||
<div class='file'>hero-detail.component.html</div>
|
||||
<div class='file'>hero-detail.component.ts</div>
|
||||
<div class='file'>hero-search.component.html (new)</div>
|
||||
<div class='file'>hero-search.component.css (new)</div>
|
||||
<div class='file'>hero-search.component.ts (new)</div>
|
||||
<div class='file'>hero-search.service.ts (new)</div>
|
||||
<div class='file'>hero.service.ts</div>
|
||||
<div class='file'>heroes.component.css</div>
|
||||
<div class='file'>heroes.component.html</div>
|
||||
<div class='file'>heroes.component.ts</div>
|
||||
<div class='file'>in-memory-data.service.ts (new)</div>
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
main.ts
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
index.html
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
styles.css
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
systemjs.config.js
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
tsconfig.json
|
||||
</div>
|
||||
|
||||
<div class='file'>main.ts</div>
|
||||
<div class='file'>index.html</div>
|
||||
<div class='file'>styles.css</div>
|
||||
<div class='file'>systemjs.config.js</div>
|
||||
<div class='file'>tsconfig.json</div>
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
node_modules ...
|
||||
</div>
|
||||
|
||||
<div class='file'>
|
||||
package.json
|
||||
</div>
|
||||
|
||||
<div class='file'>node_modules ...</div>
|
||||
<div class='file'>package.json</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
## Home Stretch
|
||||
|
||||
You're at the end of your journey, and you've accomplished a lot.
|
||||
|
@ -865,68 +536,24 @@ Here are the files you added or changed in this page.
|
|||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="app.comp...ts" path="toh-pt6/src/app/app.component.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="app.mod...ts" path="toh-pt6/src/app/app.module.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="heroes.comp...ts" path="toh-pt6/src/app/heroes.component.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="heroes.comp...html" path="toh-pt6/src/app/heroes.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="heroes.comp...css" path="toh-pt6/src/app/heroes.component.css">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-detail.comp...ts" path="toh-pt6/src/app/hero-detail.component.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-detail.comp...html" path="toh-pt6/src/app/hero-detail.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero.service.ts" path="toh-pt6/src/app/hero.service.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="in-memory-data.service.ts" path="toh-pt6/src/app/in-memory-data.service.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="app.comp...ts" path="toh-pt6/src/app/app.component.ts"></code-pane>
|
||||
<code-pane title="app.mod...ts" path="toh-pt6/src/app/app.module.ts"></code-pane>
|
||||
<code-pane title="heroes.comp...ts" path="toh-pt6/src/app/heroes.component.ts"></code-pane>
|
||||
<code-pane title="heroes.comp...html" path="toh-pt6/src/app/heroes.component.html"></code-pane>
|
||||
<code-pane title="heroes.comp...css" path="toh-pt6/src/app/heroes.component.css"></code-pane>
|
||||
<code-pane title="hero-detail.comp...ts" path="toh-pt6/src/app/hero-detail.component.ts"></code-pane>
|
||||
<code-pane title="hero-detail.comp...html" path="toh-pt6/src/app/hero-detail.component.html"></code-pane>
|
||||
<code-pane title="hero.service.ts" path="toh-pt6/src/app/hero.service.ts"></code-pane>
|
||||
<code-pane title="in-memory-data.service.ts" path="toh-pt6/src/app/in-memory-data.service.ts"></code-pane>
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="hero-search.service.ts" path="toh-pt6/src/app/hero-search.service.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-search.component.ts" path="toh-pt6/src/app/hero-search.component.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-search.component.html" path="toh-pt6/src/app/hero-search.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-search.component.css" path="toh-pt6/src/app/hero-search.component.css">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="hero-search.service.ts" path="toh-pt6/src/app/hero-search.service.ts"></code-pane>
|
||||
<code-pane title="hero-search.component.ts" path="toh-pt6/src/app/hero-search.component.ts"></code-pane>
|
||||
<code-pane title="hero-search.component.html" path="toh-pt6/src/app/hero-search.component.html"></code-pane>
|
||||
<code-pane title="hero-search.component.css" path="toh-pt6/src/app/hero-search.component.css"></code-pane>
|
||||
</code-tabs>
|
||||
|
||||
|
||||
## Next step
|
||||
|
||||
That concludes the "Tour of Heroes" tutorial.
|
||||
|
|
Loading…
Reference in New Issue