192 lines
7.1 KiB
Plaintext
192 lines
7.1 KiB
Plaintext
extends ../../../ts/_cache/tutorial/toh-pt6.jade
|
|
|
|
block includes
|
|
include ../_util-fns
|
|
- var _Http = 'BrowserClient';
|
|
- var _Angular_Http = 'Dart <code>BrowserClient</code>'
|
|
- var _httpUrl = 'https://pub.dartlang.org/packages/http'
|
|
- var _Angular_http_library = 'Dart <a href="' + _httpUrl + '"><b>http</b></a> package'
|
|
- var _HttpModule = 'BrowserClient'
|
|
- var _JSON_stringify = 'JSON.encode'
|
|
|
|
block start-server-and-watch
|
|
:marked
|
|
### Keep the app compiling and running
|
|
|
|
Open a terminal/console window.
|
|
Start the Dart compiler, watch for changes, and start our server by entering the command:
|
|
|
|
code-example(language="bash").
|
|
pub serve
|
|
|
|
block http-library
|
|
:marked
|
|
We'll be using the !{_Angular_http_library}'s
|
|
`BrowserClient` class to communicate with a server.
|
|
|
|
### Pubspec updates
|
|
|
|
Update package dependencies by adding the
|
|
`stream_transformers` and !{_Angular_http_library}s.
|
|
|
|
We also need to add a `resolved_identifiers` entry, to inform the [angular2
|
|
transformer][ng2x] that we'll be using `BrowserClient`. (For an explanation of why
|
|
this extra configuration is needed, see the [HTTP client chapter][guide-http].) We'll
|
|
also need to use `Client` from http, so let's add that now as well.
|
|
|
|
Update `pubspec.yaml` to look like this (additions are highlighted):
|
|
|
|
[guide-http]: ../guide/server-communication.html#!#http-providers
|
|
[ng2x]: https://github.com/angular/angular/wiki/Angular-2-Dart-Transformer
|
|
|
|
- var stylePattern = { pnk: /(http.*|stream.*|resolved_identifiers:|Browser.*|Client.*)/gm };
|
|
+makeExcerpt('pubspec.yaml', 'additions', null, stylePattern)
|
|
|
|
block http-providers
|
|
:marked
|
|
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
|
|
:marked
|
|
We want to replace `BrowserClient`, the service that talks to the remote server,
|
|
with the in-memory web API service.
|
|
Our in-memory web API service, shown below, is implemented using the
|
|
`http` library `MockClient` class.
|
|
All `http` client implementations share a common `Client` interface, so
|
|
we'll have our app use the `Client` type so that we can freely switch between
|
|
implementations.
|
|
|
|
block dont-be-distracted-by-backend-subst
|
|
//- No backend substitution but we do need to comment on the changes to Hero.
|
|
:marked
|
|
As is common for web API services, our mock in-memory service will be
|
|
encoding and decoding heroes in JSON format, so we enhance the `Hero`
|
|
class with these capabilities:
|
|
|
|
+makeExample('lib/hero.dart')
|
|
|
|
block get-heroes-details
|
|
:marked
|
|
To get the list of heroes, we first make an asynchronous call to
|
|
`http.get()`. Then we use the `_extractData` helper method to decode the
|
|
response body.
|
|
|
|
block observables-section-intro
|
|
:marked
|
|
Recall that `HeroService.getHeroes()` awaits for an `http.get()`
|
|
response and yields a _Future_ `List<Hero>`, which is fine when we are only
|
|
interested in a single result.
|
|
|
|
block search-criteria-intro
|
|
:marked
|
|
A [StreamController][], as its name implies, is a controller for a [Stream][] that allows us to
|
|
manipulate the underlying stream by adding data to it, for example.
|
|
|
|
In our sample, the underlying stream of strings (`_searchTerms.stream`) represents the hero
|
|
name search patterns entered by the user. Each call to `search` puts a new string into
|
|
the stream by calling `add` over the controller.
|
|
|
|
[Stream]: https://api.dartlang.org/stable/dart-async/Stream-class.html
|
|
[StreamController]: https://api.dartlang.org/stable/dart-async/StreamController-class.html
|
|
|
|
block observable-transformers
|
|
:marked
|
|
Fortunately, there are stream transformers that will help us reduce the request flow.
|
|
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
|
|
|
|
* `transform(new Debounce(... 300)))` waits until the flow of search terms pauses for 300
|
|
milliseconds before passing along the latest string. We'll never make requests more frequently
|
|
than 300ms.
|
|
|
|
* `distinct()` ensures that we only send a request if a search term has changed.
|
|
There's no point in repeating a request for the same search term.
|
|
|
|
* `transform(new FlatMapLatest(...))` applies a map-like transformer that (1) calls our search
|
|
service for each search term that makes it through the debounce and distinct gauntlet and (2)
|
|
returns only the most recent search service result, discarding any previous results.
|
|
|
|
* `handleError()` handles errors. Our simple example prints the error to the console; a real
|
|
life application should do better.
|
|
|
|
block filetree
|
|
.filetree
|
|
.file angular2_tour_of_heroes
|
|
.children
|
|
.file lib
|
|
.children
|
|
.file app_component.css
|
|
.file app_component.dart
|
|
.file dashboard_component.css
|
|
.file dashboard_component.dart
|
|
.file dashboard_component.html
|
|
.file hero.dart
|
|
.file hero_detail_component.css
|
|
.file hero_detail_component.dart
|
|
.file hero_detail_component.html
|
|
.file hero_search_component.css (new)
|
|
.file hero_search_component.dart (new)
|
|
.file hero_search_component.html (new)
|
|
.file hero_search_service.dart (new)
|
|
.file hero_service.dart
|
|
.file heroes_component.css
|
|
.file heroes_component.dart
|
|
.file heroes_component.html
|
|
.file in_memory_data_service.dart (new)
|
|
.file web
|
|
.children
|
|
.file main.dart
|
|
.file index.html
|
|
.file styles.css
|
|
.file pubspec.yaml
|
|
|
|
block file-summary
|
|
+makeTabs(
|
|
`toh-6/dart/lib/dashboard_component.dart,
|
|
toh-6/dart/lib/dashboard_component.html,
|
|
toh-6/dart/lib/hero.dart,
|
|
toh-6/dart/lib/hero_detail_component.dart,
|
|
toh-6/dart/lib/hero_detail_component.html,
|
|
toh-6/dart/lib/hero_service.dart,
|
|
toh-6/dart/lib/heroes_component.css,
|
|
toh-6/dart/lib/heroes_component.dart,
|
|
toh-6/dart/lib/in_memory_data_service.dart`,
|
|
',,,,,,,,',
|
|
`lib/dashboard_component.dart,
|
|
lib/dashboard_component.html,
|
|
lib/hero.dart,
|
|
lib/hero_detail_component.dart,
|
|
lib/hero_detail_component.html,
|
|
lib/hero_service.dart,
|
|
lib/heroes_component.css,
|
|
lib/heroes_component.dart,
|
|
lib/in_memory_data_service.dart`)
|
|
|
|
+makeTabs(
|
|
`toh-6/dart/lib/hero_search_component.css,
|
|
toh-6/dart/lib/hero_search_component.dart,
|
|
toh-6/dart/lib/hero_search_component.html,
|
|
toh-6/dart/lib/hero_search_service.dart`,
|
|
null,
|
|
`lib/hero_search_component.css,
|
|
lib/hero_search_component.dart,
|
|
lib/hero_search_component.html,
|
|
lib/hero_search_service.dart`)
|
|
|
|
+makeTabs(
|
|
`toh-6/dart/pubspec.yaml,
|
|
toh-6/dart/web/main.dart`,
|
|
null,
|
|
`pubspec.yaml,
|
|
web/main.dart`)
|