* docs(toh-6/dart): refactoring of 'add, edit, delete heroes' Refactoring of "add, edit, delete heroes" section of toh-6 from one big bottom-up step into small independent feature slices, where the user achieves a "milesone" (i.e., can run the full app) after each feature section. The section rewrite is shorter and offers a better UX. Other simplifications: - Error handling is consistent: in the hero service we log to the console, everwhere else we just let errors bubble up. - Hero service methods renamed based on function (create, update) rather then lower-level implementation (post, put). - @Output properties have been eliminated (since they weren't explained). E2E tests now pass on both the TS and Dart sides. * docs(toh-6/ts): refactoring of 'add, edit, delete heroes' Refactoring of "add, edit, delete heroes" section of toh-6 from one big bottom-up step into small independent feature slices, where the user achieves a "milesone" (i.e., can run the full app) after each feature section. The section rewrite is shorter and offers a better UX. Other simplifications: - Error handling is consistent: in the hero service we log to the console, everwhere else we just let errors bubble up. - Hero service methods renamed based on function (create, update) rather then lower-level implementation (post, put). - @Output properties have been eliminated (since they weren't explained). E2E tests now pass on both the TS and Dart sides. Post-Dart-review updates included. * docs(toh-6): ward tweaks
		
			
				
	
	
		
			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`)
 |