docs: remove rxjs-extensions in favor of explict imports (#3075)

Triggered by #2620 which it closes.
This commit is contained in:
Ward Bell 2017-01-05 01:12:06 -08:00 committed by GitHub
parent 0d5877db1c
commit aa6f503331
26 changed files with 180 additions and 210 deletions

View File

@ -1,7 +1,8 @@
// #docregion
import { Pipe, PipeTransform } from '@angular/core';
import { Http } from '@angular/http';
import './rxjs-extensions';
import 'rxjs/add/operator/map';
// #docregion pipe-metadata
@Pipe({

View File

@ -1,7 +1,10 @@
// #docregion
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import './rxjs-extensions';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
@Component({
selector: 'hero-message',

View File

@ -1,5 +0,0 @@
// Extensions to RxJS used in this app.
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

View File

@ -1,12 +1,6 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
// #docregion import-rxjs
// Add the RxJS Observable operators.
import './rxjs-operators';
// #enddocregion import-rxjs
@Component({
selector: 'my-app',
template: `
@ -17,4 +11,3 @@ import './rxjs-operators';
`
})
export class AppComponent { }
// #enddocregion

View File

@ -1,7 +1,7 @@
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';
import { AppComponent } from './app.component';

View File

@ -1,8 +1,8 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';

View File

@ -1,16 +0,0 @@
// #docregion
// import 'rxjs/Rx'; // adds ALL RxJS statics & operators to Observable
// See node_module/rxjs/Rxjs.js
// Import just the rxjs statics and operators needed for THIS app.
// Statics
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';

View File

@ -1,10 +1,15 @@
// #docplaster
// #docregion
// Promise Version
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Headers, RequestOptions } from '@angular/http';
import { Hero } from './hero';
// #docregion rxjs-imports
import 'rxjs/add/operator/toPromise';
// #enddocregion rxjs-imports
import { Hero } from './hero';
@Injectable()
export class HeroService {

View File

@ -2,16 +2,21 @@
// #docregion
// Observable Version
// #docregion v1
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
// #enddocregion v1
// #docregion import-request-options
import { Headers, RequestOptions } from '@angular/http';
// #enddocregion import-request-options
// #docregion v1
import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
// #enddocregion rxjs-imports
import { Hero } from './hero';
@Injectable()
export class HeroService {

View File

@ -1,10 +1,16 @@
/* tslint:disable: member-ordering forin */
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Component, OnInit } from '@angular/core';
// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/switchMap';
// #docregion import-subject
import { Subject } from 'rxjs/Subject';
import { Subject } from 'rxjs/Subject';
// #enddocregion import-subject
import { WikipediaService } from './wikipedia.service';
@ -12,21 +18,25 @@ import { WikipediaService } from './wikipedia.service';
@Component({
moduleId: module.id,
selector: 'my-wiki-smart',
templateUrl: './wiki.component.html',
template: `
<h1>Smarter Wikipedia Demo</h1>
<p>Search when typing stops</p>
<input #term (keyup)="search(term.value)"/>
<ul>
<li *ngFor="let item of items | async">{{item}}</li>
</ul>`,
providers: [ WikipediaService ]
})
export class WikiSmartComponent implements OnInit {
title = 'Smarter Wikipedia Demo';
fetches = 'Fetches when typing stops';
items: Observable<string[]>;
constructor (private wikipediaService: WikipediaService) {}
// #docregion subject
private searchTermStream = new Subject<string>();
search(term: string) { this.searchTermStream.next(term); }
// #enddocregion subject
constructor (private wikipediaService: WikipediaService) {}
ngOnInit() {
// #docregion observable-operators
this.items = this.searchTermStream

View File

@ -1,11 +0,0 @@
<!-- #docregion -->
<h1>{{title}}</h1>
<p><i>{{fetches}}</i></p>
<!-- #docregion keyup -->
<input #term (keyup)="search(term.value)"/>
<!-- #enddocregion keyup -->
<ul>
<li *ngFor="let item of items | async">{{item}}</li>
</ul>

View File

@ -5,19 +5,22 @@ import { Observable } from 'rxjs/Observable';
import { WikipediaService } from './wikipedia.service';
@Component({
moduleId: module.id,
selector: 'my-wiki',
templateUrl: 'wiki.component.html',
template: `
<h1>Wikipedia Demo</h1>
<p>Search after each keystroke</p>
<input #term (keyup)="search(term.value)"/>
<ul>
<li *ngFor="let item of items | async">{{item}}</li>
</ul>`,
providers: [ WikipediaService ]
})
export class WikiComponent {
title = 'Wikipedia Demo';
fetches = 'Fetches after each keystroke';
items: Observable<string[]>;
constructor (private wikipediaService: WikipediaService) { }
search (term: string) {
this.items = this.wikipediaService.search(term);
}
constructor (private wikipediaService: WikipediaService) { }
}

View File

@ -3,6 +3,8 @@
import { Injectable } from '@angular/core';
import { Jsonp } from '@angular/http';
import 'rxjs/add/operator/map';
@Injectable()
export class WikipediaService {
constructor(private jsonp: Jsonp) { }

View File

@ -2,6 +2,8 @@
import { Injectable } from '@angular/core';
import { Jsonp, URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/map';
@Injectable()
export class WikipediaService {
constructor(private jsonp: Jsonp) {}

View File

@ -1,5 +1,4 @@
// #docregion
export * from './logger.service';
export * from './rxjs-extensions';
export * from './spinner/spinner.service';
export * from './nav/nav.component';

View File

@ -1,7 +0,0 @@
// #docregion
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/of';

View File

@ -3,7 +3,11 @@
import { OnInit } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map';
import { Hero } from '../shared/hero.model';

View File

@ -1,6 +1,8 @@
// #docregion
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { Hero } from './hero.model';

View File

@ -1,6 +1,8 @@
// #docregion
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { Hero } from './hero.model';

View File

@ -1,6 +1,8 @@
// #docregion
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { Hero } from './hero.model';

View File

@ -1,9 +1,5 @@
// #docplaster
// #docregion
// #docregion rxjs-extensions
import './rxjs-extensions';
// #enddocregion rxjs-extensions
// #docregion v1, v2
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

View File

@ -2,9 +2,20 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
// Observable class extensions
import 'rxjs/add/observable/of';
// Observable operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
// #enddocregion rxjs-imports
import { HeroSearchService } from './hero-search.service';
import { Hero } from './hero';
@ -37,15 +48,15 @@ export class HeroSearchComponent implements OnInit {
ngOnInit(): void {
this.heroes = this.searchTerms
.debounceTime(300) // wait for 300ms pause in events
.debounceTime(300) // wait 300ms after each keystroke before considering the term
.distinctUntilChanged() // ignore if next search term is same as previous
.switchMap(term => term // switch to new observable each time
.switchMap(term => term // switch to new observable each time the term changes
// return the http search observable
? this.heroSearchService.search(term)
// or the observable of empty heroes if no search term
// or the observable of empty heroes if there was no search term
: Observable.of<Hero[]>([]))
.catch(error => {
// TODO: real error handling
// TODO: add real error handling
console.log(error);
return Observable.of<Hero[]>([]);
});

View File

@ -1,7 +1,9 @@
// #docregion
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Hero } from './hero';
@ -13,6 +15,6 @@ export class HeroSearchService {
search(term: string): Observable<Hero[]> {
return this.http
.get(`app/heroes/?name=${term}`)
.map((r: Response) => r.json().data as Hero[]);
.map(response => response.json().data as Hero[]);
}
}

View File

@ -1,13 +0,0 @@
// #docregion
// Observable class extensions
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';
// Observable operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';

View File

@ -55,12 +55,6 @@ block demos-list
The root `AppComponent` orchestrates these demos:
+makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts')
+ifDocsFor('ts')
:marked
There is nothing remarkable here _except_ for the import of RxJS operators, which is
described [later](#rxjs).
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.')
.l-main-section#http-providers
:marked
# Providing HTTP services
@ -196,7 +190,7 @@ a#HeroService
:marked
<a id="rxjs"></a>
If you are familiar with asynchronous methods in modern JavaScript, you might expect the `get` method to return a
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" title="Promise">promise</a>.
You'd expect to chain a call to `then()` and extract the heroes.
Instead you're calling a `map()` method.
Clearly this is not a promise.
@ -207,47 +201,24 @@ a#HeroService
.l-main-section
:marked
## RxJS library
[RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions") is a 3rd party library, endorsed by Angular,
that implements the [*asynchronous observable*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") pattern.
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS Reactive Extensions">RxJS</a>
is a third party library, endorsed by Angular, that implements the
<a href="" target="_blank" title="Video: Rob Wormald on observables"><b>asynchronous observable</b></a> pattern.
All of the Developer Guide samples have installed the RxJS npm package and loaded via `system.js`
All of the Developer Guide samples have installed the RxJS npm package
because observables are used widely in Angular applications.
The app needs it when working with the HTTP client.
Additionally, you must take a critical extra step to make RxJS observables usable.
_This_ app needs it when working with the HTTP client.
But you must take a critical extra step to make RxJS observables usable:
you must import the RxJS operators individually.
### Enable RxJS operators
The RxJS library is large.
Size matters when building a production application and deploying it to mobile devices.
You should include only necessary features.
Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable`
module that lacks most of the operators such as the `map` method you
called above in `getHeroes`.
It's up to you to add the operators you need.
You could add _every_ RxJS operator with a single import statement.
While that is the easiest thing to do, you'd pay a penalty in extended launch time
and application size because the full library is so big.
Since this app only uses a few operators, it's better to import each `Observable`
operator and static class method, one-by-one, for a custom *Observable*
implementation tuned
precisely to the app's requirements. Put the `import` statements in one `app/rxjs-operators.ts` file.
+makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".")
:marked
If you forget an operator, the TypeScript compiler warns that it's missing and you'll update this file.
.l-sub-section
:marked
The app doesn't need _all_ of these particular operators in the `HeroService` &mdash; just `map`, `catch` and `throw`.
The other operators are for later, in the *Wiki* example [below](#more-observables).
:marked
Finally, import `rxjs-operator` into `app.component.ts`:
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs', 'app/app.component.ts (import rxjs)')(format=".")
:marked
Now continue to the next section to return to the `HeroService`.
Each code file should add the operators it needs by importing from an RxJS library.
The `getHeroes` method needs the `map` and `catch` operators so it imports them like this.
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'rxjs-imports', 'app/app.component.ts (import rxjs)')(format=".")
.l-main-section
a#extract-data
@ -521,15 +492,13 @@ block wikipedia-jsonp+
:marked
### The WikiComponent
Now that you have a service that can query the Wikipedia API
turn to the component (template and class) that takes user input and displays search results.
+makeExample('server-communication/ts/app/wiki/wiki.component.html', null, 'app/wiki/wiki.component.html')
Now that you have a service that can query the Wikipedia API,
turn your attention to the component (template and class) that takes user input and displays search results.
+makeExample('server-communication/ts/app/wiki/wiki.component.ts', null, 'app/wiki/wiki.component.ts')
:marked
The template presents an `<input>` element *search box* to gather search terms from the user,
and calls a `search(term)` method after each `keyup` event.
+makeExample('server-communication/ts/app/wiki/wiki.component.html', 'keyup', 'wiki/wiki.component.html')(format='.')
:marked
The component's `search(term)` method delegates to the `WikipediaService`, which returns an
observable array of string results (`Observable<string[]>`).
Instead of subscribing to the observable inside the component, as in the `HeroListComponent`,
@ -549,7 +518,7 @@ block wikipedia-jsonp+
It is inefficient, and potentially expensive on mobile devices with limited data plans.
### 1. Wait for the user to stop typing
Presently, the code calls the server after every key stroke.
Presently, the code calls the server after every keystroke.
It should only make requests when the user *stops typing* .
Here's how it will work after refactoring:
figure.image-display
@ -569,67 +538,71 @@ block wikipedia-jsonp+
The application issues two search requests, one for *angular* and one for *http*.
Which response arrives first? It's unpredictable.
A load balancer could dispatch the requests to two different servers with different response times.
The results from the first *angular* request might arrive after the later *http* results.
The user will be confused if the *angular* results display to the *http* query.
When there are multiple requests in-flight, the app should present the responses
in the original request order. That won't happen if *angular* results arrive last.
in the original request order.
In this example, the app must always display the results for the *http* search
no matter which response arrives first.
<a id="more-observables"></a>
## More fun with observables
You can address these problems and improve the app with the help of some nifty observable operators.
You could make changes to the `WikipediaService`, but for a better
user experience, create a copy of the `WikiComponent` instead and make it smarter.
Here's the `WikiSmartComponent` which uses the same template.
user experience, create a copy of the `WikiComponent` instead and make it smarter,
with the help of some nifty observable operators.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', null, 'app/wiki/wiki-smart.component.ts')
Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`
+makeTabs(
`server-communication/ts/app/wiki/wiki-smart.component.ts,
server-communication/ts/app/wiki/wiki.component.ts`,
null,
`app/wiki/wiki-smart.component.ts,
app/wiki/wiki.component.ts`
)
:marked
While the templates are virtually identical,
there's a lot more RxJS in the "smart" version,
starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators,
imported as [described above](#rxjs).
### Create a stream of search terms
The template still binds to the search box `keyup` event and passes the complete search box value
into the component's `search` method after every user keystroke.
+makeExample('server-communication/ts/app/wiki/wiki.component.html', 'keyup', 'app/wiki/wiki.component.html (input)')(format='.')
:marked
The `WikiSmartComponent` turns the search box values into an observable _stream of search terms_
with the help of a `Subject` which you import from the RxJS observable library:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject', 'app/wiki/wiki-smart.component.ts')
The `WikiComponent` passes a new search term directly to the `WikipediaService` after every keystroke.
The `WikiSmartComponent` class turns the user's keystrokes into an observable _stream of search terms_
with the help of a `Subject`, which you import from RxJS:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject')(format='.')
:marked
The component creates a `searchTermStream` as a `Subject` of type `string`.
The `search` method adds each new search box value to that stream via the subject's `next` method.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject', 'app/wiki/wiki-smart.component.ts')(format='.')
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
:marked
### Listen for search terms
Earlier, you passed each search term directly to the service and bound the template to the service results.
Now you listen to the *stream of search terms*, manipulating the stream before it reaches the `WikipediaService`.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators',
'app/wiki/wiki-smart.component.ts')(format='.')
The `WikiSmartComponent` listens to the *stream of search terms* and
processes that stream _before_ calling the service.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
:marked
Wait for the user to stop typing for at least 300 milliseconds
([_debounceTime_](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md)).
Only changed search values make it through to the service
([_distinctUntilChanged_](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)).
* <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md" target="_blank" title="debounce operator"><i>debounceTime</i></a>
waits for the user to stop typing for at least 300 milliseconds.
The `WikipediaService` returns a separate observable of string arrays (`Observable<string[]>`) for each request.
There could be multiple requests *in-flight*, all awaiting the server's reply,
which means multiple *observables-of-strings* could arrive at any moment in any order.
* <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md" target="_blank" title="distinctUntilChanged operator"><i>distinctUntilChanged</i></a>
ensures that the service is called only when the new search term is different from the previous search term.
The [_switchMap_](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md)
(formerly known as `flatMapLatest`) returns a new observable that combines these `WikipediaService` observables,
* The <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md" target="_blank" title="switchMap operator"><i>switchMap</i></a>
calls the `WikipediaService` with a fresh, debounced search term and coordinates the stream(s) of service response.
The role of `switchMap` is particularly important.
The `WikipediaService` returns a separate observable of string arrays (`Observable<string[]>`) for each search request.
The user could issue multiple requests before a slow server has had time to reply,
which means a backlog of response observables could arrive at the client, at any moment, in any order.
The `switchMap` returns its own observable that _combines_ all `WikipediaService` response observables,
re-arranges them in their original request order,
and delivers to subscribers only the most recent search results.
The displayed list of search results stays in sync with the user's sequence of search terms.
.l-sub-section
:marked
You added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class
in `rxjs-operators` as [described above](#rxjs).
a#xsrf
.l-main-section
:marked

View File

@ -168,6 +168,10 @@ block get-heroes-details
+makeExcerpt('app/hero.service.ts', 'rxjs', '')
.l-sub-section
:marked
You'll add more operators, and learn why you must do so, [later in this tutorial](#rxjs-imports).
:marked
### Extracting the data in the *then* callback
@ -377,8 +381,9 @@ block observables-section-intro
### Background
An *observable* is a stream of events that we can process with array-like operators.
Angular core has basic support for observables. We developers augment that support with
operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library.
Angular core has basic support for observables.
We developers augment that support with operators and extensions from the
<a href="http://reactivex.io/rxjs" target="_blank" title="RxJS">RxJS library</a>.
We'll see how shortly.
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
@ -406,8 +411,15 @@ block observables-section-intro
:marked
The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one
in the `HeroService`, although the URL now has a query string.
<span if-docs="ts">Another notable difference: we no longer call `toPromise`,
we simply return the *observable* instead.</span>
<span if-docs="ts">A more important difference: we no longer call `toPromise`.
Instead we 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).
</span>
### HeroSearchComponent
@ -498,26 +510,24 @@ block observable-transformers
Our simple example prints the error to the console; a real life application should do better.
Then we return an observable containing an empty array to clear the search result.
a#rxjs-imports
:marked
### Import RxJS operators
The RxJS operators are not available in Angular's base `Observable` implementation.
We have to extend `Observable` by *importing* them.
Most RxJS operators are not included in Angular's base `Observable` implementation.
The base implementation includes only what Angular itself requires.
We could extend `Observable` with just the operators we need here by
including the pertinent `import` statements at the top of this file.
If we want more RxJS features, we have to extend `Observable` by *importing* the libraries in which they are defined.
Here are all the RxJS imports _this_ component needs:
.l-sub-section
:marked
Many authorities say we should do just that.
:marked
We take a different approach in this example.
We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file.
+makeExample('app/rxjs-extensions.ts')(format='.')
+makeExample('app/hero-search.component.ts','rxjs-imports','app/hero-search.component.ts (rxjs imports)')(format='.')
:marked
We load them all at once by importing `rxjs-extensions` at the top of `AppModule`.
The `import 'rxjs/add/...'` syntax may be unfamiliar.
It's missing the usual list of symbols between the braces: `{...}`.
+makeExcerpt('app/app.module.ts', 'rxjs-extensions')(format='.')
We 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.
:marked
### Add the search component to the dashboard
@ -570,7 +580,6 @@ block filetree
.file hero-search.component.css (new)
.file hero-search.component.ts (new)
.file hero-search.service.ts (new)
.file rxjs-extensions.ts
.file hero.service.ts
.file heroes.component.css
.file heroes.component.html
@ -625,14 +634,12 @@ block file-summary
`toh-6/ts/app/hero-search.service.ts,
toh-6/ts/app/hero-search.component.ts,
toh-6/ts/app/hero-search.component.html,
toh-6/ts/app/hero-search.component.css,
toh-6/ts/app/rxjs-extensions.ts`,
toh-6/ts/app/hero-search.component.css`,
null,
`hero-search.service.ts,
hero-search.component.ts,
hero-search.component.html,
hero-search.component.css,
rxjs-extensions.ts`
hero-search.component.css`
)
.l-sub-section