docs(toh-6): post-RC5 Dart resync and TS fixes (#2095)
* toh-6: trim spaces from cache file to simplify diff * toh-6: copy latest over cache before editing latest * docs(toh-6): post-RC5 Dart resync and TS fixes Contributes to #2077. TS-side changes include: - Merged three versions of `app/app.module{,1,2}.ts` into a single file and used docregions instead. - Misnamed files: - `rxjs-operators.ts` -> `rxjs-extensions.ts` - `hero-search.service.html` -> `hero-search.component.html` - Fixed BAD FILENAME error. Lint reports no errors and toh-6 e2e tests pass.
This commit is contained in:
parent
ea457825b8
commit
dfd46af604
|
@ -1,4 +1,4 @@
|
||||||
// #docregion , search
|
// #docregion
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
@ -6,6 +6,7 @@ import 'package:angular2/router.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
// #docregion search
|
||||||
import 'hero_search_component.dart';
|
import 'hero_search_component.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion final
|
// #docregion , v1, v2
|
||||||
// #docregion v1
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
import 'package:angular2/platform/browser.dart';
|
import 'package:angular2/platform/browser.dart';
|
||||||
import 'package:angular2_tour_of_heroes/app_component.dart';
|
import 'package:angular2_tour_of_heroes/app_component.dart';
|
||||||
|
@ -15,7 +14,7 @@ void main() {
|
||||||
// [provide(Client, useFactory: () => new BrowserClient(), deps: [])]
|
// [provide(Client, useFactory: () => new BrowserClient(), deps: [])]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// #enddocregion final
|
// #enddocregion v2,
|
||||||
/*
|
/*
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
import 'package:http/browser_client.dart';
|
import 'package:http/browser_client.dart';
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
|
|
||||||
// Imports for loading & configuring the in-memory web api
|
|
||||||
import { HttpModule, XHRBackend } from '@angular/http';
|
|
||||||
|
|
||||||
import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
|
|
||||||
import { InMemoryDataService } from './in-memory-data.service';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { routing } from './app.routing';
|
|
||||||
|
|
||||||
import { HeroesComponent } from './heroes.component';
|
|
||||||
import { DashboardComponent } from './dashboard.component';
|
|
||||||
import { HeroDetailComponent } from './hero-detail.component';
|
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
FormsModule,
|
|
||||||
routing,
|
|
||||||
HttpModule
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
HeroesComponent,
|
|
||||||
DashboardComponent,
|
|
||||||
HeroDetailComponent
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
HeroService,
|
|
||||||
{ provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
|
|
||||||
{ provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data
|
|
||||||
],
|
|
||||||
bootstrap: [ AppComponent ]
|
|
||||||
})
|
|
||||||
export class AppModule {
|
|
||||||
}
|
|
||||||
// #enddocregion final
|
|
|
@ -1,44 +0,0 @@
|
||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpModule } from '@angular/http';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { routing } from './app.routing';
|
|
||||||
|
|
||||||
import { HeroesComponent } from './heroes.component';
|
|
||||||
import { DashboardComponent } from './dashboard.component';
|
|
||||||
import { HeroDetailComponent } from './hero-detail.component';
|
|
||||||
// #docregion hero-search-declaration
|
|
||||||
import { HeroSearchComponent } from './hero-search.component';
|
|
||||||
// #enddocregion hero-search-declaration
|
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
FormsModule,
|
|
||||||
routing,
|
|
||||||
HttpModule
|
|
||||||
],
|
|
||||||
// #docregion hero-search-declaration
|
|
||||||
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
HeroesComponent,
|
|
||||||
DashboardComponent,
|
|
||||||
HeroDetailComponent,
|
|
||||||
HeroSearchComponent
|
|
||||||
],
|
|
||||||
// #enddocregion hero-search-declaration
|
|
||||||
providers: [
|
|
||||||
HeroService
|
|
||||||
],
|
|
||||||
bootstrap: [ AppComponent ]
|
|
||||||
})
|
|
||||||
export class AppModule {
|
|
||||||
}
|
|
||||||
// #enddocregion
|
|
|
@ -1,24 +1,31 @@
|
||||||
// #docregion
|
// #docplaster
|
||||||
|
// #docregion , v1, v2
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { HttpModule } from '@angular/http';
|
||||||
|
|
||||||
|
// #enddocregion v1
|
||||||
// Imports for loading & configuring the in-memory web api
|
// Imports for loading & configuring the in-memory web api
|
||||||
import { HttpModule, XHRBackend } from '@angular/http';
|
import { XHRBackend } from '@angular/http';
|
||||||
|
|
||||||
import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
|
import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
|
||||||
import { InMemoryDataService } from './in-memory-data.service';
|
import { InMemoryDataService } from './in-memory-data.service';
|
||||||
|
|
||||||
|
// #docregion v1
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { routing } from './app.routing';
|
import { routing } from './app.routing';
|
||||||
|
|
||||||
import { HeroesComponent } from './heroes.component';
|
import { HeroesComponent } from './heroes.component';
|
||||||
import { DashboardComponent } from './dashboard.component';
|
import { DashboardComponent } from './dashboard.component';
|
||||||
import { HeroDetailComponent } from './hero-detail.component';
|
import { HeroDetailComponent } from './hero-detail.component';
|
||||||
|
import { HeroService } from './hero.service';
|
||||||
|
// #enddocregion v1, v2
|
||||||
|
// #docregion search
|
||||||
import { HeroSearchComponent } from './hero-search.component';
|
import { HeroSearchComponent } from './hero-search.component';
|
||||||
|
// #docregion v1, v2
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
// #enddocregion search
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -26,20 +33,25 @@ import { HeroService } from './hero.service';
|
||||||
routing,
|
routing,
|
||||||
HttpModule
|
HttpModule
|
||||||
],
|
],
|
||||||
|
// #docregion search
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HeroesComponent,
|
HeroesComponent,
|
||||||
DashboardComponent,
|
DashboardComponent,
|
||||||
HeroDetailComponent,
|
HeroDetailComponent,
|
||||||
|
// #enddocregion v1, v2
|
||||||
HeroSearchComponent
|
HeroSearchComponent
|
||||||
|
// #docregion v1, v2
|
||||||
],
|
],
|
||||||
|
// #enddocregion search
|
||||||
providers: [
|
providers: [
|
||||||
HeroService,
|
HeroService,
|
||||||
|
// #enddocregion v1
|
||||||
{ provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
|
{ provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
|
||||||
{ provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data
|
{ provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data
|
||||||
|
// #docregion v1
|
||||||
],
|
],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ block includes
|
||||||
- var _Angular_Http = 'Dart <code>BrowserClient</code>'
|
- var _Angular_Http = 'Dart <code>BrowserClient</code>'
|
||||||
- var _httpUrl = 'https://pub.dartlang.org/packages/http'
|
- var _httpUrl = 'https://pub.dartlang.org/packages/http'
|
||||||
- var _Angular_http_library = 'Dart <a href="' + _httpUrl + '"><b>http</b></a> package'
|
- var _Angular_http_library = 'Dart <a href="' + _httpUrl + '"><b>http</b></a> package'
|
||||||
- var _HTTP_PROVIDERS = 'BrowserClient'
|
- var _HttpModule = 'BrowserClient'
|
||||||
- var _JSON_stringify = 'JSON.encode'
|
- var _JSON_stringify = 'JSON.encode'
|
||||||
|
|
||||||
block start-server-and-watch
|
block start-server-and-watch
|
||||||
|
@ -17,15 +17,15 @@ block start-server-and-watch
|
||||||
|
|
||||||
code-example(language="bash").
|
code-example(language="bash").
|
||||||
pub serve
|
pub serve
|
||||||
|
|
||||||
block http-library
|
block http-library
|
||||||
:marked
|
:marked
|
||||||
We'll be using the !{_Angular_http_library}'s
|
We'll be using the !{_Angular_http_library}'s
|
||||||
`BrowserClient` class to communicate with a server.
|
`BrowserClient` class to communicate with a server.
|
||||||
|
|
||||||
### Pubspec updates
|
### Pubspec updates
|
||||||
|
|
||||||
We need to add package dependencies for the
|
We need to add package dependencies for the
|
||||||
`stream_transformers` and !{_Angular_http_library}s.
|
`stream_transformers` and !{_Angular_http_library}s.
|
||||||
|
|
||||||
We also need to add a `resolved_identifiers` entry, to inform the [angular2
|
We also need to add a `resolved_identifiers` entry, to inform the [angular2
|
||||||
|
@ -45,10 +45,21 @@ block http-providers
|
||||||
:marked
|
:marked
|
||||||
Before our app can use `#{_Http}`, we have to register it as a service provider.
|
Before our app can use `#{_Http}`, we have to register it as a service provider.
|
||||||
|
|
||||||
|
We should be able to access `!{_Http}` services from anywhere in the application.
|
||||||
|
So we register it in the `bootstrap` call where we
|
||||||
|
launch the application and its root `AppComponent`.
|
||||||
|
|
||||||
|
+makeExcerpt('app/main.ts','v1')
|
||||||
|
|
||||||
|
:marked
|
||||||
|
Notice that we supply `!{_HttpModule}` in a list, as the second parameter to
|
||||||
|
the `bootstrap` method. This has the same effect as the `providers` list in
|
||||||
|
`@Component` annotation.
|
||||||
|
|
||||||
block backend
|
block backend
|
||||||
:marked
|
:marked
|
||||||
We want to replace `BrowserClient`, the service that talks to the remote server,
|
We want to replace `BrowserClient`, the service that talks to the remote server,
|
||||||
with the in-memory web API service.
|
with the in-memory web API service.
|
||||||
Our in-memory web API service, shown below, is implemented using the
|
Our in-memory web API service, shown below, is implemented using the
|
||||||
`http` library `MockClient` class.
|
`http` library `MockClient` class.
|
||||||
All `http` client implementations share a common `Client` interface, so
|
All `http` client implementations share a common `Client` interface, so
|
||||||
|
@ -77,7 +88,7 @@ block hero-detail-comp-updates
|
||||||
:marked
|
:marked
|
||||||
### Edit in the *HeroDetailComponent*
|
### Edit in the *HeroDetailComponent*
|
||||||
|
|
||||||
We already have `HeroDetailComponent` for viewing details about a specific hero.
|
We already have `HeroDetailComponent` for viewing details about a specific hero.
|
||||||
Supporting edit functionality is a natural extension of the detail view,
|
Supporting edit functionality is a natural extension of the detail view,
|
||||||
so we are able to reuse `HeroDetailComponent` with a few tweaks.
|
so we are able to reuse `HeroDetailComponent` with a few tweaks.
|
||||||
|
|
||||||
|
@ -87,9 +98,6 @@ block hero-detail-comp-save-and-goback
|
||||||
block add-new-hero-via-detail-comp
|
block add-new-hero-via-detail-comp
|
||||||
//- N/A
|
//- N/A
|
||||||
|
|
||||||
block heroes-comp-directives
|
|
||||||
//- N/A
|
|
||||||
|
|
||||||
block heroes-comp-add
|
block heroes-comp-add
|
||||||
//- N/A
|
//- N/A
|
||||||
|
|
||||||
|
@ -194,10 +202,10 @@ block file-summary
|
||||||
lib/hero_search_component.dart,
|
lib/hero_search_component.dart,
|
||||||
lib/hero_search_component.html,
|
lib/hero_search_component.html,
|
||||||
lib/hero_search_service.dart`)
|
lib/hero_search_service.dart`)
|
||||||
|
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`toh-6/dart/pubspec.yaml,
|
`toh-6/dart/pubspec.yaml,
|
||||||
toh-6/dart/web/main.dart`,
|
toh-6/dart/web/main.dart`,
|
||||||
`,final`,
|
null,
|
||||||
`pubspec.yaml,
|
`pubspec.yaml,
|
||||||
web/main.dart`)
|
web/main.dart`)
|
||||||
|
|
|
@ -5,7 +5,7 @@ block includes
|
||||||
- var _Http = 'Http'; // Angular `Http` library name.
|
- var _Http = 'Http'; // Angular `Http` library name.
|
||||||
- var _Angular_Http = 'Angular <code>Http</code>'
|
- var _Angular_Http = 'Angular <code>Http</code>'
|
||||||
- var _Angular_http_library = 'Angular HTTP library'
|
- var _Angular_http_library = 'Angular HTTP library'
|
||||||
- var _HTTP_PROVIDERS = 'HTTP_PROVIDERS'
|
- var _HttpModule = 'HttpModule'
|
||||||
- var _JSON_stringify = 'JSON.stringify'
|
- var _JSON_stringify = 'JSON.stringify'
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -53,25 +53,24 @@ block http-library
|
||||||
block http-providers
|
block http-providers
|
||||||
:marked
|
:marked
|
||||||
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
|
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
|
||||||
The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services.
|
The `HttpModule` from `@angular/http` library holds providers for the complete set of `http` services.
|
||||||
|
|
||||||
:marked
|
We should be able to access `http` services from anywhere in the application.
|
||||||
We should be able to access `!{_Http}` services from anywhere in the application.
|
So we register them in the `imports` array of `app.module.ts` where we
|
||||||
So we register them in the `bootstrap` call of <span ngio-ex>main.ts</span> where we
|
bootstrap the application and its root `AppComponent`.
|
||||||
launch the application and its root `AppComponent`.
|
|
||||||
|
|
||||||
+makeExcerpt('app/main.ts','v1')
|
+makeExcerpt('app/app.module.ts (v1)')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Notice that we supply `!{_HTTP_PROVIDERS}` in !{_an} !{_array} as the second parameter to the `bootstrap` method.
|
Notice that we supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`.
|
||||||
This has the same effect as the `providers` !{_array} in `@Component` !{_decorator}.
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Simulating the web API
|
## Simulating the web API
|
||||||
|
|
||||||
We generally recommend registering application-wide services in the root `AppComponent` *providers*.
|
We recommend registering application-wide services in the root
|
||||||
Here we're registering in `main` for a special reason.
|
`!{_AppModuleVsAppComp}` *providers*. <span if-docs="dart">Here we're
|
||||||
|
registering in `main` for a special reason.</span>
|
||||||
|
|
||||||
Our application is in the early stages of development and far from ready for production.
|
Our application is in the early stages of development and far from ready for production.
|
||||||
We don't even have a web server that can handle requests for heroes.
|
We don't even have a web server that can handle requests for heroes.
|
||||||
|
@ -79,12 +78,13 @@ block http-providers
|
||||||
|
|
||||||
We're going to *trick* the HTTP client into fetching and saving data from
|
We're going to *trick* the HTTP client into fetching and saving data from
|
||||||
a mock service, the *in-memory web API*.
|
a mock service, the *in-memory web API*.
|
||||||
|
<span if-docs="dart"> The application itself doesn't need to know and
|
||||||
|
shouldn't know about this. So we'll slip the in-memory web API into the
|
||||||
|
configuration *above* the `AppComponent`.</span>
|
||||||
|
|
||||||
The application itself doesn't need to know and shouldn't know about this.
|
Here is a version of <span ngio-ex>!{_appModuleTsVsMainTs}</span> that performs this trick:
|
||||||
So we'll slip the in-memory web API into the configuration *above* the `AppComponent`.
|
|
||||||
|
|
||||||
Here is a version of `main` that performs this trick
|
+makeExcerpt(_appModuleTsVsMainTs, 'v2')
|
||||||
+makeExcerpt('app/main.ts', 'final')
|
|
||||||
|
|
||||||
block backend
|
block backend
|
||||||
:marked
|
:marked
|
||||||
|
@ -133,7 +133,7 @@ block get-heroes-details
|
||||||
*Observables* are a powerful way to manage asynchronous data flows.
|
*Observables* are a powerful way to manage asynchronous data flows.
|
||||||
We'll learn about [Observables](#observables) later in this chapter.
|
We'll learn about [Observables](#observables) later in this chapter.
|
||||||
|
|
||||||
For *now* we get back on familiar ground by immediately by
|
For *now* we get back on familiar ground by immediately by
|
||||||
converting that `Observable` to a `Promise` using the `toPromise` operator.
|
converting that `Observable` to a `Promise` using the `toPromise` operator.
|
||||||
+makeExcerpt('app/hero.service.ts', 'to-promise', '')
|
+makeExcerpt('app/hero.service.ts', 'to-promise', '')
|
||||||
:marked
|
:marked
|
||||||
|
@ -326,19 +326,6 @@ block add-new-hero-via-detail-comp
|
||||||
Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions used in the template.
|
Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions used in the template.
|
||||||
Let's start with *add*.
|
Let's start with *add*.
|
||||||
|
|
||||||
block heroes-comp-directives
|
|
||||||
:marked
|
|
||||||
We're using the `HeroDetailComponent` to capture the new hero information.
|
|
||||||
We have to tell Angular about that by importing the `HeroDetailComponent` and referencing it in the component metadata `directives` array.
|
|
||||||
+makeExcerpt('app/heroes.component.ts (HeroDetailComponent)', 'hero-detail-component')
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
These are the same lines that we removed in the previous [Routing](toh-pt5.html) chapter.
|
|
||||||
We didn't know at the time that we'd need the *HeroDetailComponent* again. So we tidied up.
|
|
||||||
|
|
||||||
Now we *must* put these lines back. If we don't, Angular will ignore the `<my-hero-detail>`
|
|
||||||
tag and pushing the *Add New Hero* button will have no visible effect.
|
|
||||||
:marked
|
|
||||||
Implement the click handler for the *Add New Hero* button.
|
Implement the click handler for the *Add New Hero* button.
|
||||||
|
|
||||||
+makeExcerpt('app/heroes.component.ts', 'addHero')
|
+makeExcerpt('app/heroes.component.ts', 'addHero')
|
||||||
|
@ -372,7 +359,7 @@ block observables-section-intro
|
||||||
Each `Http` method returns an `Observable` of HTTP `Response` objects.
|
Each `Http` method returns an `Observable` of HTTP `Response` objects.
|
||||||
|
|
||||||
Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
|
Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
|
||||||
In this section we learn to return the `Observable` directly and discuss when and why that might be
|
In this section we learn to return the `Observable` directly and discuss when and why that might be
|
||||||
a good thing to do.
|
a good thing to do.
|
||||||
|
|
||||||
### Background
|
### Background
|
||||||
|
@ -385,15 +372,15 @@ block observables-section-intro
|
||||||
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
|
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
|
||||||
That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller.
|
That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller.
|
||||||
|
|
||||||
Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data.
|
Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data.
|
||||||
When we receive the data, we're done.
|
When we receive the data, we're done.
|
||||||
A single result in the form of a promise is easy for the calling component to consume
|
A single result in the form of a promise is easy for the calling component to consume
|
||||||
and it helps that promises are widely understood by JavaScript programmers.
|
and it helps that promises are widely understood by JavaScript programmers.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
But requests aren't always "one and done". We may start one request,
|
But requests aren't always "one and done". We may start one request,
|
||||||
then cancel it, and make a different request before the server has responded to the first request.
|
then cancel it, and make a different request before the server has responded to the first request.
|
||||||
Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*.
|
Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*.
|
||||||
It's easy with *!{_Observable}s* as we'll see.
|
It's easy with *!{_Observable}s* as we'll see.
|
||||||
|
|
||||||
### Search-by-name
|
### Search-by-name
|
||||||
|
@ -407,12 +394,12 @@ block observables-section-intro
|
||||||
:marked
|
:marked
|
||||||
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">Another notable difference: we no longer call `toPromise`,
|
<span if-docs="ts">Another notable difference: we no longer call `toPromise`,
|
||||||
we simply return the *observable* instead.</span>
|
we simply return the *observable* instead.</span>
|
||||||
|
|
||||||
### HeroSearchComponent
|
### HeroSearchComponent
|
||||||
|
|
||||||
Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`.
|
Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`.
|
||||||
|
|
||||||
The component template is simple — just a text box and a list of matching search results.
|
The component template is simple — just a text box and a list of matching search results.
|
||||||
|
|
||||||
|
@ -435,7 +422,7 @@ block observables-section-intro
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
#### Search terms
|
#### Search terms
|
||||||
|
|
||||||
Let's focus on the `!{_priv}searchTerms`:
|
Let's focus on the `!{_priv}searchTerms`:
|
||||||
|
|
||||||
+makeExcerpt('app/hero-search.component.ts', 'searchTerms', '')
|
+makeExcerpt('app/hero-search.component.ts', 'searchTerms', '')
|
||||||
|
@ -466,7 +453,7 @@ block observable-transformers
|
||||||
Fortunately, we can chain `Observable` operators to the string `Observable` that reduce the request flow.
|
Fortunately, we can chain `Observable` operators to the string `Observable` that reduce the request flow.
|
||||||
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
|
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
|
||||||
|
|
||||||
* `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. We'll never make requests more frequently than 300ms.
|
before passing along the latest string. We'll never make requests more frequently than 300ms.
|
||||||
|
|
||||||
* `distinctUntilChanged` ensures that we only send a request if the filter text changed.
|
* `distinctUntilChanged` ensures that we only send a request if the filter text changed.
|
||||||
|
@ -481,21 +468,21 @@ block observable-transformers
|
||||||
(formerly known as "flatMapLatest") is very clever.
|
(formerly known as "flatMapLatest") is very clever.
|
||||||
|
|
||||||
Every qualifying key event can trigger an http call.
|
Every qualifying key event can trigger an http call.
|
||||||
Even with a 300ms pause between requests, we could have multiple http requests in flight
|
Even with a 300ms pause between requests, we 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 call.
|
only the observable from the most recent http call.
|
||||||
Results from prior calls are canceled and discarded.
|
Results from prior calls are canceled and discarded.
|
||||||
|
|
||||||
We also short-circuit the http call and return an observable containing an empty array
|
We also short-circuit the http call and return an observable containing an empty array
|
||||||
if the search text is empty.
|
if the search text is empty.
|
||||||
|
|
||||||
Note that _canceling_ the `HeroSearchService` observable won't actually abort a pending http request
|
Note that _canceling_ the `HeroSearchService` observable won't actually abort a pending http request
|
||||||
until the service supports that feature, a topic for another day.
|
until the service supports that feature, a topic for another day.
|
||||||
We are content for now to discard unwanted results.
|
We are content for now to discard unwanted results.
|
||||||
:marked
|
:marked
|
||||||
* `catch` intercepts a failed observable.
|
* `catch` intercepts a failed observable.
|
||||||
Our simple example prints the error to the console; a real life application should do better.
|
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.
|
Then we return an observable containing an empty array to clear the search result.
|
||||||
|
|
||||||
|
@ -510,7 +497,7 @@ block observable-transformers
|
||||||
:marked
|
:marked
|
||||||
Many authorities say we should do just that.
|
Many authorities say we should do just that.
|
||||||
:marked
|
:marked
|
||||||
We take a different approach in this example.
|
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.
|
We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file.
|
||||||
|
|
||||||
+makeExample('app/rxjs-extensions.ts')
|
+makeExample('app/rxjs-extensions.ts')
|
||||||
|
@ -527,10 +514,12 @@ block observable-transformers
|
||||||
|
|
||||||
+makeExample('app/dashboard.component.html')
|
+makeExample('app/dashboard.component.html')
|
||||||
|
|
||||||
|
- var _declarations = _docsFor == 'dart' ? 'directives' : 'declarations'
|
||||||
|
- var declFile = _docsFor == 'dart' ? 'app/dashboard.component.ts' : 'app/app.module.ts'
|
||||||
:marked
|
:marked
|
||||||
And finally, we import the `HeroSearchComponent` and add it to the `directives` !{_array}.
|
And finally, we import the `HeroSearchComponent` and add it to the `!{_declarations}` !{_array}:
|
||||||
|
|
||||||
+makeExcerpt('app/dashboard.component.ts', 'search')
|
+makeExcerpt(declFile, 'search')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Run the app again, go to the *Dashboard*, and enter some text in the search box.
|
Run the app again, go to the *Dashboard*, and enter some text in the search box.
|
||||||
|
@ -554,7 +543,8 @@ block filetree
|
||||||
.children
|
.children
|
||||||
.file app.component.ts
|
.file app.component.ts
|
||||||
.file app.component.css
|
.file app.component.css
|
||||||
.file app.routes.ts
|
.file app.module.ts
|
||||||
|
.file app.routing.ts
|
||||||
.file dashboard.component.css
|
.file dashboard.component.css
|
||||||
.file dashboard.component.html
|
.file dashboard.component.html
|
||||||
.file dashboard.component.ts
|
.file dashboard.component.ts
|
||||||
|
@ -566,7 +556,7 @@ block filetree
|
||||||
.file hero-search.component.css (new)
|
.file hero-search.component.css (new)
|
||||||
.file hero-search.component.ts (new)
|
.file hero-search.component.ts (new)
|
||||||
.file hero-search.service.ts (new)
|
.file hero-search.service.ts (new)
|
||||||
.file rxjs-operators.ts
|
.file rxjs-extensions.ts
|
||||||
.file hero.service.ts
|
.file hero.service.ts
|
||||||
.file heroes.component.css
|
.file heroes.component.css
|
||||||
.file heroes.component.html
|
.file heroes.component.html
|
||||||
|
@ -593,12 +583,13 @@ block filetree
|
||||||
- We updated our components to allow adding, editing and deleting of heroes.
|
- We updated our components to allow adding, editing and deleting of heroes.
|
||||||
- We configured an in-memory web API.
|
- We configured an in-memory web API.
|
||||||
- We learned how to use !{_Observable}s.
|
- We learned how to use !{_Observable}s.
|
||||||
|
|
||||||
Here are the files we added or changed in this chapter.
|
Here are the files we added or changed in this chapter.
|
||||||
|
|
||||||
block file-summary
|
block file-summary
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`toh-6/ts/app/app.component.ts,
|
`toh-6/ts/app/app.component.ts,
|
||||||
|
toh-6/ts/app/app.module.ts,
|
||||||
toh-6/ts/app/heroes.component.ts,
|
toh-6/ts/app/heroes.component.ts,
|
||||||
toh-6/ts/app/heroes.component.html,
|
toh-6/ts/app/heroes.component.html,
|
||||||
toh-6/ts/app/heroes.component.css,
|
toh-6/ts/app/heroes.component.css,
|
||||||
|
@ -606,8 +597,9 @@ block file-summary
|
||||||
toh-6/ts/app/hero-detail.component.html,
|
toh-6/ts/app/hero-detail.component.html,
|
||||||
toh-6/ts/app/hero.service.ts,
|
toh-6/ts/app/hero.service.ts,
|
||||||
toh-6/ts/app/in-memory-data.service.ts`,
|
toh-6/ts/app/in-memory-data.service.ts`,
|
||||||
null,
|
',,,,,,,,',
|
||||||
`app.comp...ts,
|
`app.comp...ts,
|
||||||
|
app.mod...ts,
|
||||||
heroes.comp...ts,
|
heroes.comp...ts,
|
||||||
heroes.comp...html,
|
heroes.comp...html,
|
||||||
heroes.comp...css,
|
heroes.comp...css,
|
||||||
|
@ -622,11 +614,11 @@ block file-summary
|
||||||
toh-6/ts/app/hero-search.component.ts,
|
toh-6/ts/app/hero-search.component.ts,
|
||||||
toh-6/ts/app/hero-search.component.html,
|
toh-6/ts/app/hero-search.component.html,
|
||||||
toh-6/ts/app/hero-search.component.css,
|
toh-6/ts/app/hero-search.component.css,
|
||||||
toh-6/ts/app/rxjs-operators.ts`,
|
toh-6/ts/app/rxjs-extensions.ts`,
|
||||||
null,
|
null,
|
||||||
`hero-search.service.ts,
|
`hero-search.service.ts,
|
||||||
hero-search.component.ts,
|
hero-search.component.ts,
|
||||||
hero-search.service.html,
|
hero-search.component.html,
|
||||||
hero-search.component.css,
|
hero-search.component.css,
|
||||||
rxjs-operators.ts`
|
rxjs-extensions.ts`
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,6 @@ block includes
|
||||||
- var _Http = 'Http'; // Angular `Http` library name.
|
- var _Http = 'Http'; // Angular `Http` library name.
|
||||||
- var _Angular_Http = 'Angular <code>Http</code>'
|
- var _Angular_Http = 'Angular <code>Http</code>'
|
||||||
- var _Angular_http_library = 'Angular HTTP library'
|
- var _Angular_http_library = 'Angular HTTP library'
|
||||||
- var _HTTP_PROVIDERS = 'HTTP_PROVIDERS'
|
|
||||||
- var _HttpModule = 'HttpModule'
|
- var _HttpModule = 'HttpModule'
|
||||||
- var _JSON_stringify = 'JSON.stringify'
|
- var _JSON_stringify = 'JSON.stringify'
|
||||||
|
|
||||||
|
@ -54,23 +53,24 @@ block http-library
|
||||||
block http-providers
|
block http-providers
|
||||||
:marked
|
:marked
|
||||||
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
|
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
|
||||||
The `HttpModule` from `@angular/http` library holds providers for the complete set of http services.
|
The `HttpModule` from `@angular/http` library holds providers for the complete set of `http` services.
|
||||||
|
|
||||||
:marked
|
We should be able to access `http` services from anywhere in the application.
|
||||||
We should be able to access `!{_Http}` services from anywhere in the application.
|
So we register them in the `imports` array of `app.module.ts` where we
|
||||||
So we register them in the `imports` array of <span ngio-ex>app.module.ts</span> where we
|
bootstrap the application and its root `AppComponent`.
|
||||||
bootstrap the application and its root `AppComponent`.
|
|
||||||
|
|
||||||
+makeExample('app/app.module.2.ts','', 'app.module.ts (v.1)')
|
+makeExcerpt('app/app.module.ts (v1)')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Notice that we supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`.
|
Notice that we supply `!{_HttpModule}` as part of the *imports* !{_array} in root NgModule `AppModule`.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Simulating the web API
|
## Simulating the web API
|
||||||
|
|
||||||
We recommend registering application-wide services in the root `NgModule` *providers*.
|
We recommend registering application-wide services in the root
|
||||||
|
`!{_AppModuleVsAppComp}` *providers*. <span if-docs="dart">Here we're
|
||||||
|
registering in `main` for a special reason.</span>
|
||||||
|
|
||||||
Our application is in the early stages of development and far from ready for production.
|
Our application is in the early stages of development and far from ready for production.
|
||||||
We don't even have a web server that can handle requests for heroes.
|
We don't even have a web server that can handle requests for heroes.
|
||||||
|
@ -78,9 +78,13 @@ block http-providers
|
||||||
|
|
||||||
We're going to *trick* the HTTP client into fetching and saving data from
|
We're going to *trick* the HTTP client into fetching and saving data from
|
||||||
a mock service, the *in-memory web API*.
|
a mock service, the *in-memory web API*.
|
||||||
|
<span if-docs="dart"> The application itself doesn't need to know and
|
||||||
|
shouldn't know about this. So we'll slip the in-memory web API into the
|
||||||
|
configuration *above* the `AppComponent`.</span>
|
||||||
|
|
||||||
Here is a version of `app.module.ts` that performs this trick
|
Here is a version of <span ngio-ex>!{_appModuleTsVsMainTs}</span> that performs this trick:
|
||||||
+makeExample('app/app.module.1.ts', '', 'app.module.ts (final)')
|
|
||||||
|
+makeExcerpt(_appModuleTsVsMainTs, 'v2')
|
||||||
|
|
||||||
block backend
|
block backend
|
||||||
:marked
|
:marked
|
||||||
|
@ -510,11 +514,12 @@ block observable-transformers
|
||||||
|
|
||||||
+makeExample('app/dashboard.component.html')
|
+makeExample('app/dashboard.component.html')
|
||||||
|
|
||||||
|
- var _declarations = _docsFor == 'dart' ? 'directives' : 'declarations'
|
||||||
|
- var declFile = _docsFor == 'dart' ? 'app/dashboard.component.ts' : 'app/app.module.ts'
|
||||||
:marked
|
:marked
|
||||||
And finally, we import the `HeroSearchComponent` and add it to the `declarations` !{_array} in
|
And finally, we import the `HeroSearchComponent` and add it to the `!{_declarations}` !{_array}:
|
||||||
`app.module.ts`.
|
|
||||||
|
|
||||||
+makeExcerpt('app/app.module.2.ts', 'hero-search-declaration')
|
+makeExcerpt(declFile, 'search')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Run the app again, go to the *Dashboard*, and enter some text in the search box.
|
Run the app again, go to the *Dashboard*, and enter some text in the search box.
|
||||||
|
@ -551,7 +556,7 @@ block filetree
|
||||||
.file hero-search.component.css (new)
|
.file hero-search.component.css (new)
|
||||||
.file hero-search.component.ts (new)
|
.file hero-search.component.ts (new)
|
||||||
.file hero-search.service.ts (new)
|
.file hero-search.service.ts (new)
|
||||||
.file rxjs-operators.ts
|
.file rxjs-extensions.ts
|
||||||
.file hero.service.ts
|
.file hero.service.ts
|
||||||
.file heroes.component.css
|
.file heroes.component.css
|
||||||
.file heroes.component.html
|
.file heroes.component.html
|
||||||
|
@ -609,12 +614,11 @@ block file-summary
|
||||||
toh-6/ts/app/hero-search.component.ts,
|
toh-6/ts/app/hero-search.component.ts,
|
||||||
toh-6/ts/app/hero-search.component.html,
|
toh-6/ts/app/hero-search.component.html,
|
||||||
toh-6/ts/app/hero-search.component.css,
|
toh-6/ts/app/hero-search.component.css,
|
||||||
toh-6/ts/app/rxjs-operators.ts`,
|
toh-6/ts/app/rxjs-extensions.ts`,
|
||||||
null,
|
null,
|
||||||
`hero-search.service.ts,
|
`hero-search.service.ts,
|
||||||
hero-search.component.ts,
|
hero-search.component.ts,
|
||||||
hero-search.service.html,
|
hero-search.component.html,
|
||||||
hero-search.component.css,
|
hero-search.component.css,
|
||||||
hero-search.component.css,
|
rxjs-extensions.ts`
|
||||||
rxjs-operators.ts`
|
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue