docs(toh-6/dart): first edition of prose and example code (#1687)
* docs(toh-6/dart): first edition of prose and example code NOTE: this PR depends on #1686. Dart prose and example match TS except that: - No child-to-parent event emission occurs. - Support for Add Hero is added as an unconditional feature of the Heroes view. - http `_post` takes only a name - http `delete` takes only a hero id. - The Dart in-memory-data-service has been dropped in favor of an implementation based on the "standard" `http.testing.MockClient` class. * post-review changes
This commit is contained in:
		
							parent
							
								
									97fbda0d76
								
							
						
					
					
						commit
						f06398cd89
					
				
							
								
								
									
										5
									
								
								public/docs/_examples/toh-6/dart/.docsync.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								public/docs/_examples/toh-6/dart/.docsync.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | { | ||||||
|  |   "title": "Tour of Heroes: HTTP", | ||||||
|  |   "docPart": "tutorial", | ||||||
|  |   "docHref": "toh-pt6.html" | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								public/docs/_examples/toh-6/dart/lib/app_component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								public/docs/_examples/toh-6/dart/lib/app_component.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | /* #docregion */ | ||||||
|  | h1 { | ||||||
|  |   font-size: 1.2em; | ||||||
|  |   color: #999; | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | h2 { | ||||||
|  |   font-size: 2em; | ||||||
|  |   margin-top: 0; | ||||||
|  |   padding-top: 0; | ||||||
|  | } | ||||||
|  | nav a { | ||||||
|  |   padding: 5px 10px; | ||||||
|  |   text-decoration: none; | ||||||
|  |   margin-top: 10px; | ||||||
|  |   display: inline-block; | ||||||
|  |   background-color: #eee; | ||||||
|  |   border-radius: 4px; | ||||||
|  | } | ||||||
|  | nav a:visited, a:link { | ||||||
|  |   color: #607D8B; | ||||||
|  | } | ||||||
|  | nav a:hover { | ||||||
|  |   color: #039be5; | ||||||
|  |   background-color: #CFD8DC; | ||||||
|  | } | ||||||
|  | nav a.router-link-active { | ||||||
|  |   color: #039be5; | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								public/docs/_examples/toh-6/dart/lib/app_component.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								public/docs/_examples/toh-6/dart/lib/app_component.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | // #docplaster | ||||||
|  | // #docregion | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | import 'package:angular2/router.dart'; | ||||||
|  | 
 | ||||||
|  | import 'package:angular2_tour_of_heroes/heroes_component.dart'; | ||||||
|  | import 'package:angular2_tour_of_heroes/hero_service.dart'; | ||||||
|  | import 'package:angular2_tour_of_heroes/dashboard_component.dart'; | ||||||
|  | // #docregion hero-detail-import | ||||||
|  | import 'package:angular2_tour_of_heroes/hero_detail_component.dart'; | ||||||
|  | // #enddocregion hero-detail-import | ||||||
|  | 
 | ||||||
|  | @Component( | ||||||
|  |     selector: 'my-app', | ||||||
|  |     // #docregion template | ||||||
|  |     template: ''' | ||||||
|  |       <h1>{{title}}</h1> | ||||||
|  |       <nav> | ||||||
|  |         <a [routerLink]="['Dashboard']">Dashboard</a> | ||||||
|  |         <a [routerLink]="['Heroes']">Heroes</a> | ||||||
|  |       </nav> | ||||||
|  |       <router-outlet></router-outlet>''', | ||||||
|  |     // #enddocregion template | ||||||
|  |     // #docregion style-urls | ||||||
|  |     styleUrls: const ['app_component.css'], | ||||||
|  |     // #enddocregion style-urls | ||||||
|  |     directives: const [ROUTER_DIRECTIVES], | ||||||
|  |     providers: const [HeroService, ROUTER_PROVIDERS]) | ||||||
|  | @RouteConfig(const [ | ||||||
|  |   // #docregion dashboard-route | ||||||
|  |   const Route( | ||||||
|  |       path: '/dashboard', | ||||||
|  |       name: 'Dashboard', | ||||||
|  |       component: DashboardComponent, | ||||||
|  |       useAsDefault: true), | ||||||
|  |   // #enddocregion dashboard-route | ||||||
|  |   // #docregion hero-detail-route | ||||||
|  |   const Route( | ||||||
|  |       path: '/detail/:id', name: 'HeroDetail', component: HeroDetailComponent), | ||||||
|  |   // #enddocregion hero-detail-route | ||||||
|  |   const Route(path: '/heroes', name: 'Heroes', component: HeroesComponent) | ||||||
|  | ]) | ||||||
|  | class AppComponent { | ||||||
|  |   String title = 'Tour of Heroes'; | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								public/docs/_examples/toh-6/dart/lib/dashboard_component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								public/docs/_examples/toh-6/dart/lib/dashboard_component.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | /* #docregion */ | ||||||
|  | [class*='col-'] { | ||||||
|  |   float: left; | ||||||
|  | } | ||||||
|  | *, *:after, *:before { | ||||||
|  | 	-webkit-box-sizing: border-box; | ||||||
|  | 	-moz-box-sizing: border-box; | ||||||
|  | 	box-sizing: border-box; | ||||||
|  | } | ||||||
|  | h3 { | ||||||
|  |   text-align: center; margin-bottom: 0; | ||||||
|  | } | ||||||
|  | [class*='col-'] { | ||||||
|  |   padding-right: 20px; | ||||||
|  |   padding-bottom: 20px; | ||||||
|  | } | ||||||
|  | [class*='col-']:last-of-type { | ||||||
|  |   padding-right: 0; | ||||||
|  | } | ||||||
|  | .grid { | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .col-1-4 { | ||||||
|  |   width: 25%; | ||||||
|  | } | ||||||
|  | .module { | ||||||
|  | 	padding: 20px; | ||||||
|  | 	text-align: center; | ||||||
|  | 	color: #eee; | ||||||
|  | 	max-height: 120px; | ||||||
|  | 	min-width: 120px; | ||||||
|  | 	background-color: #607D8B; | ||||||
|  | 	border-radius: 2px; | ||||||
|  | } | ||||||
|  | h4 { | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | .module:hover { | ||||||
|  |   background-color: #EEE; | ||||||
|  |   cursor: pointer; | ||||||
|  |   color: #607d8b; | ||||||
|  | } | ||||||
|  | .grid-pad { | ||||||
|  |   padding: 10px 0; | ||||||
|  | } | ||||||
|  | .grid-pad > [class*='col-']:last-of-type { | ||||||
|  |   padding-right: 20px; | ||||||
|  | } | ||||||
|  | @media (max-width: 600px) { | ||||||
|  | 	.module { | ||||||
|  | 	  font-size: 10px; | ||||||
|  | 	  max-height: 75px; } | ||||||
|  | } | ||||||
|  | @media (max-width: 1024px) { | ||||||
|  | 	.grid { | ||||||
|  | 	  margin: 0; | ||||||
|  | 	} | ||||||
|  | 	.module { | ||||||
|  | 	  min-width: 60px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,47 @@ | |||||||
|  | // #docplaster | ||||||
|  | // #docregion | ||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | // #docregion import-router | ||||||
|  | import 'package:angular2/router.dart'; | ||||||
|  | // #enddocregion import-router | ||||||
|  | 
 | ||||||
|  | import 'hero.dart'; | ||||||
|  | import 'hero_service.dart'; | ||||||
|  | 
 | ||||||
|  | @Component( | ||||||
|  |     selector: 'my-dashboard', | ||||||
|  |     // #docregion template-url | ||||||
|  |     templateUrl: 'dashboard_component.html', | ||||||
|  |     // #enddocregion template-url | ||||||
|  |     // #docregion css | ||||||
|  |     styleUrls: const ['dashboard_component.css'] | ||||||
|  |     // #enddocregion css | ||||||
|  |     ) | ||||||
|  | // #docregion component | ||||||
|  | class DashboardComponent implements OnInit { | ||||||
|  |   List<Hero> heroes; | ||||||
|  | 
 | ||||||
|  |   // #docregion ctor | ||||||
|  |   final Router _router; | ||||||
|  |   final HeroService _heroService; | ||||||
|  | 
 | ||||||
|  |   DashboardComponent(this._heroService, this._router); | ||||||
|  | 
 | ||||||
|  |   // #enddocregion ctor | ||||||
|  | 
 | ||||||
|  |   Future<Null> ngOnInit() async { | ||||||
|  |     heroes = (await _heroService.getHeroes()).skip(1).take(4).toList(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // #docregion goto-detail | ||||||
|  |   void gotoDetail(Hero hero) { | ||||||
|  |     var link = [ | ||||||
|  |       'HeroDetail', | ||||||
|  |       {'id': hero.id.toString()} | ||||||
|  |     ]; | ||||||
|  |     _router.navigate(link); | ||||||
|  |   } | ||||||
|  | // #enddocregion goto-detail | ||||||
|  | } | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | <!-- #docregion --> | ||||||
|  | <h3>Top Heroes</h3> | ||||||
|  | <div class="grid grid-pad"> | ||||||
|  |   <!-- #docregion click --> | ||||||
|  |   <div *ngFor="let hero of heroes" (click)="gotoDetail(hero)" class="col-1-4" > | ||||||
|  |     <!-- #enddocregion click --> | ||||||
|  |     <div class="module hero"> | ||||||
|  |       <h4>{{hero.name}}</h4> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										14
									
								
								public/docs/_examples/toh-6/dart/lib/hero.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								public/docs/_examples/toh-6/dart/lib/hero.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | // #docregion | ||||||
|  | class Hero { | ||||||
|  |   final int id; | ||||||
|  |   String name; | ||||||
|  | 
 | ||||||
|  |   Hero(this.id, this.name); | ||||||
|  | 
 | ||||||
|  |   factory Hero.fromJson(Map<String, dynamic> hero) => | ||||||
|  |     new Hero(_toInt(hero['id']), hero['name']); | ||||||
|  | 
 | ||||||
|  |   Map toJson() => {'id': id, 'name': name}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int _toInt(id) => id is int ? id : int.parse(id); | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | /* #docregion */ | ||||||
|  | label { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 3em; | ||||||
|  |   margin: .5em 0; | ||||||
|  |   color: #607D8B; | ||||||
|  |   font-weight: bold; | ||||||
|  | } | ||||||
|  | input { | ||||||
|  |   height: 2em; | ||||||
|  |   font-size: 1em; | ||||||
|  |   padding-left: .4em; | ||||||
|  | } | ||||||
|  | button { | ||||||
|  |   margin-top: 20px; | ||||||
|  |   font-family: Arial; | ||||||
|  |   background-color: #eee; | ||||||
|  |   border: none; | ||||||
|  |   padding: 5px 10px; | ||||||
|  |   border-radius: 4px; | ||||||
|  |   cursor: pointer; cursor: hand; | ||||||
|  | } | ||||||
|  | button:hover { | ||||||
|  |   background-color: #cfd8dc; | ||||||
|  | } | ||||||
|  | button:disabled { | ||||||
|  |   background-color: #eee; | ||||||
|  |   color: #ccc;  | ||||||
|  |   cursor: auto; | ||||||
|  | } | ||||||
| @ -0,0 +1,61 @@ | |||||||
|  | // #docplaster | ||||||
|  | // #docregion , v2 | ||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:html'; | ||||||
|  | 
 | ||||||
|  | // #docregion import-oninit | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | // #enddocregion import-oninit | ||||||
|  | // #docregion import-route-params | ||||||
|  | import 'package:angular2/router.dart'; | ||||||
|  | // #enddocregion import-route-params | ||||||
|  | 
 | ||||||
|  | import 'hero.dart'; | ||||||
|  | // #docregion import-hero-service | ||||||
|  | import 'hero_service.dart'; | ||||||
|  | // #enddocregion import-hero-service | ||||||
|  | 
 | ||||||
|  | // #docregion extract-template | ||||||
|  | @Component( | ||||||
|  |     selector: 'my-hero-detail', | ||||||
|  |     // #docregion template-url | ||||||
|  |     templateUrl: 'hero_detail_component.html', | ||||||
|  |     // #enddocregion template-url, v2 | ||||||
|  |     styleUrls: const ['hero_detail_component.css'] | ||||||
|  |     // #docregion v2 | ||||||
|  |     ) | ||||||
|  | // #enddocregion extract-template | ||||||
|  | // #docregion implement | ||||||
|  | class HeroDetailComponent implements OnInit { | ||||||
|  |   // #enddocregion implement | ||||||
|  |   Hero hero; | ||||||
|  |   // #docregion ctor | ||||||
|  |   final HeroService _heroService; | ||||||
|  |   final RouteParams _routeParams; | ||||||
|  | 
 | ||||||
|  |   HeroDetailComponent(this._heroService, this._routeParams); | ||||||
|  |   // #enddocregion ctor | ||||||
|  | 
 | ||||||
|  |   // #docregion ng-oninit | ||||||
|  |   Future<Null> ngOnInit() async { | ||||||
|  |     // #docregion get-id | ||||||
|  |     var idString = _routeParams.get('id'); | ||||||
|  |     var id = int.parse(idString, onError: (_) => null); | ||||||
|  |     // #enddocregion get-id | ||||||
|  |     if (id != null) hero = await (_heroService.getHero(id)); | ||||||
|  |   } | ||||||
|  |   // #enddocregion ng-oninit | ||||||
|  | 
 | ||||||
|  |   // #docregion save | ||||||
|  |   Future<Null> save() async { | ||||||
|  |     await _heroService.save(hero); | ||||||
|  |     goBack(); | ||||||
|  |   } | ||||||
|  |   // #enddocregion save | ||||||
|  | 
 | ||||||
|  |   // #docregion go-back | ||||||
|  |   void goBack() { | ||||||
|  |     window.history.back(); | ||||||
|  |   } | ||||||
|  |   // #enddocregion go-back | ||||||
|  | } | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | <!-- #docplaster --> | ||||||
|  | <!-- #docregion --> | ||||||
|  | <div *ngIf="hero != null"> | ||||||
|  |   <h2>{{hero.name}} details!</h2> | ||||||
|  |   <div> | ||||||
|  |     <label>id: </label>{{hero.id}}</div> | ||||||
|  |   <div> | ||||||
|  |     <label>name: </label> | ||||||
|  |     <input [(ngModel)]="hero.name" placeholder="name" /> | ||||||
|  |   </div> | ||||||
|  |   <button (click)="goBack()">Back</button> | ||||||
|  |   <!-- #docregion save --> | ||||||
|  |   <button (click)="save()">Save</button> | ||||||
|  |   <!-- #enddocregion save --> | ||||||
|  | </div> | ||||||
							
								
								
									
										91
									
								
								public/docs/_examples/toh-6/dart/lib/hero_service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								public/docs/_examples/toh-6/dart/lib/hero_service.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | // #docregion | ||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | 
 | ||||||
|  | import 'hero.dart'; | ||||||
|  | 
 | ||||||
|  | @Injectable() | ||||||
|  | class HeroService { | ||||||
|  |   // #docregion post | ||||||
|  |   static final _headers = {'Content-Type': 'application/json'}; | ||||||
|  |   // #enddocregion post | ||||||
|  |   // #docregion getHeroes | ||||||
|  |   static const _heroesUrl = 'app/heroes'; // URL to web API | ||||||
|  | 
 | ||||||
|  |   final Client _http; | ||||||
|  | 
 | ||||||
|  |   HeroService(this._http); | ||||||
|  | 
 | ||||||
|  |   Future<List<Hero>> getHeroes() async { | ||||||
|  |     try { | ||||||
|  |       final response = await _http.get(_heroesUrl); | ||||||
|  |       final heroes = _extractData(response) | ||||||
|  |           .map((value) => new Hero.fromJson(value)) | ||||||
|  |           .toList(); | ||||||
|  |       return heroes; | ||||||
|  |       // #docregion catch | ||||||
|  |     } catch (e) { | ||||||
|  |       throw _handleError(e); | ||||||
|  |     } | ||||||
|  |     // #enddocregion catch | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // #docregion extract-data | ||||||
|  |   dynamic _extractData(Response resp) => JSON.decode(resp.body)['data']; | ||||||
|  |   // #enddocregion extract-data, getHeroes | ||||||
|  | 
 | ||||||
|  |   Future<Hero> getHero(int id) async => | ||||||
|  |       (await getHeroes()).firstWhere((hero) => hero.id == id); | ||||||
|  | 
 | ||||||
|  |   // #docregion save | ||||||
|  |   Future<Hero> save(dynamic heroOrName) => | ||||||
|  |       heroOrName is Hero ? _put(heroOrName) : _post(heroOrName); | ||||||
|  |   // #enddocregion save | ||||||
|  | 
 | ||||||
|  |   // #docregion handleError | ||||||
|  |   Exception _handleError(dynamic e) { | ||||||
|  |     print(e); // for demo purposes only | ||||||
|  |     return new Exception('Server error; cause: $e'); | ||||||
|  |   } | ||||||
|  |   // #enddocregion handleError | ||||||
|  | 
 | ||||||
|  |   // #docregion post | ||||||
|  |   Future<Hero> _post(String name) async { | ||||||
|  |     try { | ||||||
|  |       final response = await _http.post(_heroesUrl, | ||||||
|  |           headers: _headers, body: JSON.encode({'name': name})); | ||||||
|  |       return new Hero.fromJson(_extractData(response)); | ||||||
|  |     } catch (e) { | ||||||
|  |       throw _handleError(e); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // #enddocregion post | ||||||
|  | 
 | ||||||
|  |   // #docregion put | ||||||
|  |   Future<Hero> _put(Hero hero) async { | ||||||
|  |     try { | ||||||
|  |       var url = '$_heroesUrl/${hero.id}'; | ||||||
|  |       final response = | ||||||
|  |           await _http.put(url, headers: _headers, body: JSON.encode(hero)); | ||||||
|  |       return new Hero.fromJson(_extractData(response)); | ||||||
|  |     } catch (e) { | ||||||
|  |       throw _handleError(e); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // #enddocregion put | ||||||
|  | 
 | ||||||
|  |   // #docregion delete | ||||||
|  |   Future<Null> delete(int id) async { | ||||||
|  |     try { | ||||||
|  |       var url = '$_heroesUrl/$id'; | ||||||
|  |       await _http.delete(url, headers: _headers); | ||||||
|  |     } catch (e) { | ||||||
|  |       throw _handleError(e); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // #enddocregion delete | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | .selected { | ||||||
|  |   background-color: #CFD8DC !important; | ||||||
|  |   color: white; | ||||||
|  | } | ||||||
|  | .heroes { | ||||||
|  |   margin: 0 0 2em 0; | ||||||
|  |   list-style-type: none; | ||||||
|  |   padding: 0; | ||||||
|  |   width: 15em; | ||||||
|  | } | ||||||
|  | .heroes li { | ||||||
|  |   cursor: pointer; | ||||||
|  |   position: relative; | ||||||
|  |   left: 0; | ||||||
|  |   background-color: #EEE; | ||||||
|  |   margin: .5em; | ||||||
|  |   padding: .3em 0; | ||||||
|  |   height: 1.6em; | ||||||
|  |   border-radius: 4px; | ||||||
|  | } | ||||||
|  | .heroes li:hover { | ||||||
|  |   color: #607D8B; | ||||||
|  |   background-color: #DDD; | ||||||
|  |   left: .1em; | ||||||
|  | } | ||||||
|  | .heroes li.selected:hover { | ||||||
|  |   background-color: #BBD8DC !important; | ||||||
|  |   color: white; | ||||||
|  | } | ||||||
|  | .heroes .text { | ||||||
|  |   position: relative; | ||||||
|  |   top: -3px; | ||||||
|  | } | ||||||
|  | .heroes .badge { | ||||||
|  |   display: inline-block; | ||||||
|  |   font-size: small; | ||||||
|  |   color: white; | ||||||
|  |   padding: 0.8em 0.7em 0 0.7em; | ||||||
|  |   background-color: #607D8B; | ||||||
|  |   line-height: 1em; | ||||||
|  |   position: relative; | ||||||
|  |   left: -1px; | ||||||
|  |   top: -4px; | ||||||
|  |   height: 1.8em; | ||||||
|  |   margin-right: .8em; | ||||||
|  |   border-radius: 4px 0 0 4px; | ||||||
|  | } | ||||||
|  | button { | ||||||
|  |   font-family: Arial; | ||||||
|  |   background-color: #eee; | ||||||
|  |   border: none; | ||||||
|  |   padding: 5px 10px; | ||||||
|  |   border-radius: 4px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   cursor: hand; | ||||||
|  | } | ||||||
|  | button:hover { | ||||||
|  |   background-color: #cfd8dc; | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | // #docplaster | ||||||
|  | // #docregion | ||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | import 'package:angular2/router.dart'; | ||||||
|  | 
 | ||||||
|  | import 'hero.dart'; | ||||||
|  | import 'hero_detail_component.dart'; | ||||||
|  | import 'hero_service.dart'; | ||||||
|  | 
 | ||||||
|  | @Component( | ||||||
|  |     selector: 'my-heroes', | ||||||
|  |     templateUrl: 'heroes_component.html', | ||||||
|  |     styleUrls: const ['heroes_component.css'], | ||||||
|  |     directives: const [HeroDetailComponent]) | ||||||
|  | class HeroesComponent implements OnInit { | ||||||
|  |   final Router _router; | ||||||
|  |   final HeroService _heroService; | ||||||
|  |   List<Hero> heroes; | ||||||
|  |   Hero selectedHero; | ||||||
|  |   // #docregion error | ||||||
|  |   String errorMessage; | ||||||
|  |   // #enddocregion error | ||||||
|  | 
 | ||||||
|  |   HeroesComponent(this._heroService, this._router); | ||||||
|  | 
 | ||||||
|  |   // #docregion addHero | ||||||
|  |   Future<Null> addHero(String name) async { | ||||||
|  |     name = name.trim(); | ||||||
|  |     if (name.isEmpty) return; | ||||||
|  |     try { | ||||||
|  |       heroes.add(await _heroService.save(name)); | ||||||
|  |     } catch (e) { | ||||||
|  |       errorMessage = e.toString(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // #enddocregion addHero | ||||||
|  | 
 | ||||||
|  |   // #docregion deleteHero | ||||||
|  |   Future<Null> deleteHero(int id, event) async { | ||||||
|  |     try { | ||||||
|  |       event.stopPropagation(); | ||||||
|  |       await _heroService.delete(id); | ||||||
|  |       heroes.removeWhere((hero) => hero.id == id); | ||||||
|  |       if (selectedHero?.id == id) selectedHero = null; | ||||||
|  |     } catch (e) { | ||||||
|  |       errorMessage = e.toString(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // #enddocregion deleteHero | ||||||
|  | 
 | ||||||
|  |   Future<Null> getHeroes() async { | ||||||
|  |     heroes = await _heroService.getHeroes(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void ngOnInit() { | ||||||
|  |     getHeroes(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void onSelect(Hero hero) { | ||||||
|  |     selectedHero = hero; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Null> gotoDetail() => _router.navigate([ | ||||||
|  |         'HeroDetail', | ||||||
|  |         {'id': selectedHero.id.toString()} | ||||||
|  |       ]); | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								public/docs/_examples/toh-6/dart/lib/heroes_component.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | <!-- #docplaster --> | ||||||
|  | <!-- #docregion --> | ||||||
|  | <h2>My Heroes</h2> | ||||||
|  | <!-- #docregion add-and-error --> | ||||||
|  | <div class="error" *ngIf="errorMessage != null">{{errorMessage}}</div> | ||||||
|  | <div> | ||||||
|  |   Name: <input #newHeroName /> | ||||||
|  |   <button (click)="addHero(newHeroName.value); newHeroName.value=''"> | ||||||
|  |     Add New Hero | ||||||
|  |   </button> | ||||||
|  | </div> | ||||||
|  | <!-- #enddocregion add-and-error --> | ||||||
|  | <ul class="heroes"> | ||||||
|  |   <li *ngFor="let hero of heroes" | ||||||
|  |     [class.selected]="hero === selectedHero" | ||||||
|  |     (click)="onSelect(hero)"> | ||||||
|  |     <span class="badge">{{hero.id}}</span> {{hero.name}} | ||||||
|  |     <!-- #docregion delete --> | ||||||
|  |     <button class="delete-button" (click)="deleteHero(hero.id, $event)">x</button> | ||||||
|  |     <!-- #enddocregion delete --> | ||||||
|  |   </li> | ||||||
|  | </ul> | ||||||
|  | <!-- #docregion mini-detail --> | ||||||
|  | <div *ngIf="selectedHero != null"> | ||||||
|  |   <h2> | ||||||
|  |     <!-- #docregion pipe --> | ||||||
|  |     {{selectedHero.name | uppercase}} is my hero | ||||||
|  |     <!-- #enddocregion pipe --> | ||||||
|  |   </h2> | ||||||
|  |   <button (click)="gotoDetail()">View Details</button> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,64 @@ | |||||||
|  | // #docregion | ||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | // #docregion init | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:http/testing.dart'; | ||||||
|  | 
 | ||||||
|  | import 'hero.dart'; | ||||||
|  | 
 | ||||||
|  | @Injectable() | ||||||
|  | class InMemoryDataService extends MockClient { | ||||||
|  |   static final _initialHeroes = [ | ||||||
|  |     {'id': 11, 'name': 'Mr. Nice'}, | ||||||
|  |     {'id': 12, 'name': 'Narco'}, | ||||||
|  |     {'id': 13, 'name': 'Bombasto'}, | ||||||
|  |     {'id': 14, 'name': 'Celeritas'}, | ||||||
|  |     {'id': 15, 'name': 'Magneta'}, | ||||||
|  |     {'id': 16, 'name': 'RubberMan'}, | ||||||
|  |     {'id': 17, 'name': 'Dynama2'}, | ||||||
|  |     {'id': 18, 'name': 'Dr IQ'}, | ||||||
|  |     {'id': 19, 'name': 'Magma'}, | ||||||
|  |     {'id': 20, 'name': 'Tornado'} | ||||||
|  |   ]; | ||||||
|  |   // #enddocregion init | ||||||
|  | 
 | ||||||
|  |   static final List<Hero> _heroesDb = | ||||||
|  |       _initialHeroes.map((json) => new Hero.fromJson(json)).toList(); | ||||||
|  |   static int _nextId = 21; | ||||||
|  | 
 | ||||||
|  |   static Future<Response> _handler(Request request) async { | ||||||
|  |     var data; | ||||||
|  |     switch (request.method) { | ||||||
|  |       case 'GET': | ||||||
|  |         data = _heroesDb; | ||||||
|  |         break; | ||||||
|  |       case 'POST': | ||||||
|  |         var name = JSON.decode(request.body)['name']; | ||||||
|  |         var newHero = new Hero(_nextId++, name); | ||||||
|  |         _heroesDb.add(newHero); | ||||||
|  |         data = newHero; | ||||||
|  |         break; | ||||||
|  |       case 'PUT': | ||||||
|  |         var heroChanges = new Hero.fromJson(JSON.decode(request.body)); | ||||||
|  |         var targetHero = _heroesDb.firstWhere((h) => h.id == heroChanges.id); | ||||||
|  |         targetHero.name = heroChanges.name; | ||||||
|  |         data = targetHero; | ||||||
|  |         break; | ||||||
|  |       case 'DELETE': | ||||||
|  |         var id = int.parse(request.url.pathSegments.last); | ||||||
|  |         _heroesDb.removeWhere((hero) => hero.id == id); | ||||||
|  |         // No data, so leave it as null. | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         throw 'Unimplemented HTTP method ${request.method}'; | ||||||
|  |     } | ||||||
|  |     return new Response(JSON.encode({'data': data}), 200, | ||||||
|  |         headers: {'content-type': 'application/json'}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   InMemoryDataService() : super(_handler); | ||||||
|  |   // #docregion init | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								public/docs/_examples/toh-6/dart/pubspec.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								public/docs/_examples/toh-6/dart/pubspec.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | # #docregion , additions | ||||||
|  | name: angular2_tour_of_heroes | ||||||
|  |   # #enddocregion additions | ||||||
|  | description: Tour of Heroes | ||||||
|  | version: 0.0.1 | ||||||
|  | environment: | ||||||
|  |   sdk: '>=1.13.0 <2.0.0' | ||||||
|  |   # #docregion additions | ||||||
|  | dependencies: | ||||||
|  |   angular2: 2.0.0-beta.17 | ||||||
|  |   # #enddocregion additions | ||||||
|  |   browser: ^0.10.0 | ||||||
|  |   dart_to_js_script_rewriter: ^1.0.1 | ||||||
|  |   # #docregion additions | ||||||
|  |   http: ^0.11.0 | ||||||
|  | transformers: | ||||||
|  | - angular2: | ||||||
|  |     # #enddocregion additions | ||||||
|  |     platform_directives: | ||||||
|  |     - 'package:angular2/common.dart#COMMON_DIRECTIVES' | ||||||
|  |     platform_pipes: | ||||||
|  |     - 'package:angular2/common.dart#COMMON_PIPES' | ||||||
|  |     # #docregion additions | ||||||
|  |     entry_points: web/main.dart | ||||||
|  |     resolved_identifiers: | ||||||
|  |         BrowserClient: 'package:http/browser_client.dart' | ||||||
|  |         Client: 'package:http/http.dart' | ||||||
|  | - dart_to_js_script_rewriter | ||||||
							
								
								
									
										18
									
								
								public/docs/_examples/toh-6/dart/web/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								public/docs/_examples/toh-6/dart/web/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <!-- #docregion --> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <base href="/"> | ||||||
|  |     <title>Angular 2 Tour of Heroes</title> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  | 
 | ||||||
|  |     <link rel="stylesheet" href="styles.css"> | ||||||
|  |     <link rel="stylesheet" href="sample.css"> | ||||||
|  | 
 | ||||||
|  |     <script defer src="main.dart" type="application/dart"></script> | ||||||
|  |     <script defer src="packages/browser/dart.js"></script> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <my-app>Loading...</my-app> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										29
									
								
								public/docs/_examples/toh-6/dart/web/main.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								public/docs/_examples/toh-6/dart/web/main.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | // #docplaster | ||||||
|  | // #docregion final | ||||||
|  | // #docregion v1 | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | import 'package:angular2/platform/browser.dart'; | ||||||
|  | import 'package:angular2_tour_of_heroes/app_component.dart'; | ||||||
|  | // #enddocregion v1 | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:angular2_tour_of_heroes/in_memory_data_service.dart'; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   bootstrap(AppComponent, | ||||||
|  |       const [const Provider(Client, useClass: InMemoryDataService)]); | ||||||
|  | } | ||||||
|  | // #enddocregion final | ||||||
|  | /* | ||||||
|  | // #docregion v1 | ||||||
|  | import 'package:http/browser_client.dart'; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   bootstrap(AppComponent, [ | ||||||
|  |     provide(BrowserClient, useFactory: () => new BrowserClient(), deps: []) | ||||||
|  |   ]); | ||||||
|  |   // Simplify bootstrap provider list to [BrowserClient] | ||||||
|  |   // once there is a fix for: | ||||||
|  |   // https://github.com/angular/angular/issues/9673 | ||||||
|  | } | ||||||
|  | // #enddocregion v1 | ||||||
|  | */ | ||||||
							
								
								
									
										7
									
								
								public/docs/_examples/toh-6/dart/web/sample.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/docs/_examples/toh-6/dart/web/sample.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | /* #docregion */ | ||||||
|  | .error {color:red;} | ||||||
|  | button.delete-button { | ||||||
|  |   float:right; | ||||||
|  |   background-color: gray !important; | ||||||
|  |   color:white; | ||||||
|  | } | ||||||
| @ -1 +1,141 @@ | |||||||
| != partial("../../../_includes/_ts-temp") | extends ../../../ts/latest/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 _HTTP_PROVIDERS = '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 | ||||||
|  |      | ||||||
|  |     We need to add a package dependency for the !{_Angular_http_library}. | ||||||
|  | 
 | ||||||
|  |     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.*|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. | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | 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 payload (`body`). | ||||||
|  | 
 | ||||||
|  | block hero-detail-comp-extra-imports-and-vars | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | block hero-detail-comp-updates | ||||||
|  |   :marked | ||||||
|  |     ### Edit in the *HeroDetailComponent* | ||||||
|  | 
 | ||||||
|  |     We already have `HeroDetailComponent` for viewing details about a specific hero.  | ||||||
|  |     Supporting edit functionality is a natural extension of the detail view, | ||||||
|  |     so we are able to reuse `HeroDetailComponent` with a few tweaks. | ||||||
|  | 
 | ||||||
|  | block hero-detail-comp-save-and-goback | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | block add-new-hero-via-detail-comp | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | block heroes-comp-directives | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | block heroes-comp-add | ||||||
|  |   //- N/A | ||||||
|  | 
 | ||||||
|  | block review | ||||||
|  |   //- Not showing animated gif due to differences between TS and Dart implementations. | ||||||
|  | 
 | ||||||
|  | block filetree | ||||||
|  |   .filetree | ||||||
|  |     .file angular2-tour-of-heroes | ||||||
|  |     .children | ||||||
|  |       .file lib | ||||||
|  |       .children | ||||||
|  |         .file app_component.dart | ||||||
|  |         .file app_component.css | ||||||
|  |         .file dashboard_component.css | ||||||
|  |         .file dashboard_component.html | ||||||
|  |         .file dashboard_component.dart | ||||||
|  |         .file hero.dart | ||||||
|  |         .file hero_detail_component.css | ||||||
|  |         .file hero_detail_component.html | ||||||
|  |         .file hero_detail_component.dart | ||||||
|  |         .file hero_service.dart | ||||||
|  |         .file heroes_component.css | ||||||
|  |         .file heroes_component.html | ||||||
|  |         .file heroes_component.dart | ||||||
|  |         .file main.dart | ||||||
|  |         .file in_memory_data_service.dart (new) | ||||||
|  |       .file web | ||||||
|  |       .children | ||||||
|  |         .file main.dart | ||||||
|  |         .file index.html | ||||||
|  |         .file sample.css (new) | ||||||
|  |         .file styles.css | ||||||
|  |       .file pubspec.yaml | ||||||
|  | 
 | ||||||
|  | block file-summary | ||||||
|  |   +makeTabs( | ||||||
|  |     `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.dart, | ||||||
|  |      toh-6/dart/web/index.html, | ||||||
|  |      toh-6/dart/web/main.dart, | ||||||
|  |      toh-6/dart/web/sample.css`, | ||||||
|  |     `,,,,,,final,`, | ||||||
|  |     `lib/hero.dart, | ||||||
|  |      lib/hero_detail_component.dart, | ||||||
|  |      lib/hero_detail_component.html, | ||||||
|  |      lib/hero_service.dart, | ||||||
|  |      lib/heroes_component.dart, | ||||||
|  |      web/index.html, | ||||||
|  |      web/main.dart, | ||||||
|  |      web/sample.css`) | ||||||
|  | 
 | ||||||
|  |   +makeExample('pubspec.yaml') | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user