Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

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

{
  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 { }
<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)を登録する

}

まとめ

  • なんか普通にむずいな。。。