Angular2 The Tour of Heroes tutorial に挑戦してみる 5
ROUTING 前半戦
https://angular.io/docs/ts/latest/tutorial/toh-pt5.html
Add the Angular component router and learn to navigate among the views.
- Add a Dashboard view. ダッシュボードビューの作成
- Add the ability to navigate between the Heroes and Dashboard views. ダッシュボードと、Heroesの間をnavigateするabilityを追加する
- When users click a hero name in either view, navigate to a detail view of the selected hero. hero nameをクリックした時に、detail view of selected hero へとnavigateする
- When users click a deep link in an email, open the detail view for a particular hero. deep link in email をクリックした時に、open the detail view for a paticular hero.
Action plan
- Turn AppComponent into an application shell that only handles navigation. AppComponentをnavigationだけをhandleするapplicationに変更する
- Relocate the Heroes concerns within the current AppComponent to a separate HeroesComponent. AppComponent内の Heroesに関わる部分は、HeroesComponentとして分ける
- Add routing. routingを追加する
- Create a new DashboardComponent. DashboardComponentを作成する
- Tie the Dashboard into the navigation structure. Dashboardを navigation structureとつなぐ
Routing is another name for navigation. The router is the mechanism for navigating from view to view.
このあたりは、djangoのviewsの処理と似てるかもな(^^)
Splitting the AppComponent
- The AppComponent should only handle navigation, so you’ll move the display of Heroes out of AppComponent and into its own HeroesComponent. AppComponentは、navigationだけを担当するようにする。
HeroesComponent
- 現時点で、AppComponentは、HeroesComponentようなもんなので、これをrenameする。
- AppComponentは別途ファイルを作成する
@Component({ selector: 'my-heroes', }) export class HeroesComponent implements OnInit { }
Create AppComponent
import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: ` <h1>{{title}}</h1> <my-heroes></my-heroes> ` }) export class AppComponent { title = 'Tour of Heroes'; }
- djangoのviewsをmixins を継承して利用するようなイメージでいいのかな。
- template内に、他のcomponentで設定した、selectorを利用できる。
- ただ、そのためには、selector, componentをapp.module.tsに設定しておく必要あり
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { HeroDetailComponent } from './hero-detail.component'; import { HeroesComponent } from './heroes.component'; import { HeroService } from './hero.service'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, HeroDetailComponent, HeroesComponent ], providers: [ HeroService ], bootstrap: [ AppComponent ] }) export class AppModule { }
- providers で HeroServiceも設定している?なぜだ。
- Add HeroService to the providers array of AppModule because you’ll need it in every other view.とのことらしい。どこからでも参照できるように、AppModuleに加えておいたほうがいいらしい。
- 重複するため、Remove HeroService from the HeroesComponent providers array とのこと。
Add routing
- RouterModuleを使う。The router is a combination of multiple provided services (RouterModule), multiple directives (RouterOutlet, RouterLink, RouterLinkActive), and a configuration (Routes). とのことらしい。
- index.htmlに、
<base href="/">
があることを確認する。
Configure routes
import { RouterModule } from '@angular/router'; ... RouterModule.forRoot([ { path: 'heroes', component: HeroesComponent } ])
- RouterModuleを書く場所は、imports[]の中。
- path は、 アクセスするurl
- componentは、routerが起動したときに、作られるべきcomponentを書く
- forRoot()メソッドは、configured routerがprovided at the apps root なので、呼び出されているらしい。意味がわからん。
- The forRoot() method supplies the Router service providers and directives needed for routing, and performs the initial navigation based on the current browser URL. 要は、routingに必要なメソッドで、現在のURLにもとづいて、initial navigation を実行するメソッドとのこと。
Router outlet
/heroes
をブラウザに貼り付けた場合 the routerは、herose routeとマッチして、HeroesComponentを表示する必要がある。ただ、そのためには、routerがどこに、componentを表示すればいいかを指定する必要がある。- そのために、<router-outlet>を使う。templateの最後に入れる
- RouterOutletは、RouterModuleから提供されるdirectivesの中の1つ。
- the routerは、
<router-outlet>
にcomponentを表示する - 要は、呼び出したcomponentを表示するのが、router-outletでOK?
Router links
- たぶんそのままで、routerへのlinks
- app.component.tsを修正すると以下のようになる。
template: ` <h1>{{title}}</h1> <a routerLink="/heroes">Heroes</a> <router-outlet></router-outlet> `
/heroes
のpathは、さっきapp.module.tsで設定したpath: 'heroes',
と紐付いている- このtemplateは、RouterModuleで、設定した
path :'heroes'
へのlinkをrouterLinkで設定して、pathにアクセスした時は、設定したcomponent: HeroesComponent
を <router-outlet>の箇所に呼び出すという機能がある。 - これはわかりやすい!これで、AppComponentが、attached to a router and displays routed views.
- こういう、router機能だけを主にもつものをrouter componentというらしい。これは、djangoでは、urls.pyに近いのかな。
Add a dashboard
- dashbord.component.ts を作成する
import { Component } from '@angular/core'; @Component({ selector: 'my-dashboard', template: '<h3>My Dashboard</h3>' }) export class DashboardComponent{}
Configure the dashboard route
- dashboard routeを追加する
{ path: 'dashboard', component: DashboardComponent },
- Also import and add DashboardComponent to the AppModule’s declarations.
declarations: [ AppComponent, DashboardComponent, HeroDetailComponent, HeroesComponent ],
Add a redirect route
- dashboard viewを
/
のときに表示させるには、redirect を使う
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
Add navigation to the template
app.component.ts
のtemplateにdashboardのrouteを追記する。
template: ` <h1>{{title}}</h1> <nav> <a routerLink="/dashboard">Dashboard</a> <a routerLink="/heroes">Heroes</a> </nav> <router-outlet></router-outlet> `
- いまのところ
Add heroes to the dashboard
- templateをtemplateUrlに変更してみる。 これは、djangoのviewのtemplateとほぼおなじ意味かな。
import { Component } from '@angular/core'; @Component({ selector: 'my-dashboard', templateUrl: './dashboard.component.html' }) export class DashboardComponent { }
- dashboard.component.htmlを作成する
<h3>Top Heroes</h3> <div class="grid grid-pad"> <div *ngFor="let hero of heroes" class="col-1-4"> <div class="module hero"> <h4>{{hero.name}}</h4> </div> </div> </div>
- heroとか読み込んでないので、まだ何も表示されないはず. これから、HeroServiceなどを再利用して、templateを完成させていく。
Get Heroese
- dashboard.component.tsに、必要なmodule, componentをimportする
import { Component, OnInit } from '@angular/core'; import { Hero } from './hero'; import { HeroService } from './hero.service';
- さらに、export class DashboardComponentを以下のように修正する。
export class DashboardComponent implements OnInit { // OnInitを実装(継承) heroes: Hero[] = []; // heroesの型(class)を定義する 初期値は空にしておく。 constructor(private heroService: HeroService) { } //private変数を設定する ngOnInit(): void { //voidでreturnの無い関数をセットする this.heroService.getHeroes() // getHeroesが成功した場合 this.herose にheroesの1〜5をスライスしたリストを代入する .then(heroes => this.heroes = heroes.slice(1, 5)); } }
Navigating to hero details
- HeroDetailComponentにアクセスできるようにする。
From the dashboard to a selected hero.
From the heroes list to a selected hero.
From a "deep link" URL pasted into the browser address bar.
Routing to a hero detail
<hero-detail [hero]="selectedHero"></hero-detail>
これではうまくいかない。
Parameterized route
/detail/11
のように、detail viewに、hero のidをつけるapp.module.tsに追記する。
{ path: 'detail/:id', component: HeroDetailComponent },
Revise the HeroDetailComponent
// Keep the Input import for now, you'll remove it later: import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { Location } from '@angular/common'; import { HeroService } from './hero.service';
- Inject the ActivatedRoute, HeroService, and Location services into the constructor, saving their values in private fields:
constructor( private heroService: HeroService, private route: ActivatedRoute, private location: Location ) {}
- Import the switchMap operator to use later with the route parameters Observable.
import 'rxjs/add/operator/switchMap';
- OnInit Interfaceを実装する。
export class HeroDetailComponent implements OnInit.
- params からidをゲットして、idで指定したheroを表示するようにする。
ngOnInit(): void { this.route.params //paramsは、もともとあるのかな? .switchMap((params: Params) => this.heroService.getHero(+params['id'])) // switchMapで、もともとのparamsのidをgetHeroでgetしたidに変更する // idはnumberだけど、route paramsは、文字列じゃないといけないから、javascriptの+operattorで、文字列に変換してる .subscribe(hero => this.hero = hero); // subscribeの意味は不明 // subscribeは3つの関数を引数に取る。(success, error, complete) // subscribe:変更を通知してもらうオブジェクト(subscripter)を登録する }
まとめ
- なんか普通にむずいな。。。