このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

web帳

記事詳細

2017.04.06

Angular2の始め方。Angular2 公式チュートリアル – Routing(簡単な和訳)

ROUTING

こちらのページは、以下のページの簡単な和訳です。

Angular2 tutorial | ROUTING

前回
Angular2の始め方。Angular2 公式チュートリアル – Services(簡単な和訳)

その他

Angular2の始め方。Angular2 公式チュートリアル – Multiple Components(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – Master/Detail(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – SETUP FOR LOCAL DEVELOPMENT(簡単な和訳)Angular2の始め方。Angular2 公式チュートリアル – Introduction(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – The Hero Editor(簡単な和訳)

ヒーローズツアーの新しい要件があります:

・ ダッシュボードビューを追加します。

・ HeroesビューとDashboardビューの間をナビゲートする機能を追加します。

・ いずれかのビューでユーザーがヒーロー名をクリックすると、選択したヒーローの詳細ビューに移動します。

・ ユーザーが電子メールのディープリンクをクリックすると、特定のヒーローの詳細ビューが開きます。

完了したら、ユーザーは次のようにアプリをナビゲートできます。

これらの要件を満たすために、アプリにAngularのルータを追加します。

“““
ルータの詳細については、Routing and Navigationページを参照してください。
“““

このページが終了したら、アプリは、live example / downloadable example. のようになります。

“““
ライブサンプルのブラウザのアドレスバーでURLの変更を確認するには、右上のアイコンをクリックしてPlunkerエディタでもう一度開き、右上隅の青い「X」ボタンをクリックしてプレビューウィンドウを開きます。


“““

Where you left off

ヒーローツアーに進む前に、以下の構造を持っていることを確認してください。

angular-tour-of-heroes
├──src
│ ├──app
│ │  ├──app.component.ts
│ │  ├──app.module.ts
│ │  ├──hero.ts
│ │  ├──hero-detail.component.ts
│ │  ├──hero.service.ts
│ │  └──mock-heroes.ts
│ ├──main.ts
│ ├──index.html
│ ├──styles.css
│ ├──systemjs.config.js
│ └──tsconfig.json
├──node_modules ...
└──package.json

Keep the app transpiling and running

次のコマンドを入力します。

$ npm start

このコマンドは、”watch mode”で、TypeScriptコンパイラを実行し、コードが変更されたときに自動的に再コンパイルします。

このコマンドは、ブラウザでアプリを同時に起動し、コードが変更されたときにブラウザを更新します。

ブラウザを再コンパイルまたは更新するために一時停止することなく、ヒーローのツアーを構築し続けることができます。

Action plan

計画は次の通りとなります。

AppComponentをナビゲーションのみを処理するアプリケーションシェルに変換します。

・ 現在のAppComponent内のHeroesの懸念事項を、別のHeroesComponentに再配置します。

・ ルーティングを追加します。

・ 新しいDashboardComponentを作成します。

・ ダッシュボードをナビゲーション構造に結び付けます。

“““
ルーティングはナビゲーションの別の名前です。ルータは、ビューからビューにナビゲートするためのメカニズムです。
“““

Splitting the AppComponent

現在のAppは、AppComponentをロードし、すぐにヒーローのリストを表示します。

修正されたアプリケーションは、ビューの選択肢を持つシェル(ダッシュボードとヒーローズ)を表示し、そのうちの1つにデフォルトを設定する必要があります。

AppComponentはナビゲーションのみを処理する必要があります。

そのため、Heroの表示をAppComponentからHeroesComponentに移動します

HeroesComponent

AppComponentはすでにHeroes専用です。

AppComponentからコードを移動する代わりに、HeroesComponentの名前を変更し、別のAppComponentシェルを作成します。

次の操作を行います。

app.component.tsファイルの名前をheroes.component.tsに変更します。

AppComponentクラスの名前をHeroesComponentに変更します(このファイル内でのみローカルに名前を変更します)。

・ セレクタmy-appの名前を、my-heroesに変更します。

src/app/heroes.component.ts (showing renamings only)

@Component({
  selector: 'my-heroes',
})
export class HeroesComponent implements OnInit {
}
Create AppComponent

新しい AppComponentはアプリケーションシェルです。

ユーザーがナビゲートするページの上部にはナビゲーションリンクが、下部には表示領域があります。

次の手順を実行します。

  • src / app / app.component.tsファイルを作成します。
  • エクスポートされた AppComponentクラスを定義します。
  • my-appセレクタを使用して、クラスの上に@Componentデコレータを追加します。
  • HeroesComponentから、AppComponentに次の項目を移動します。
    • タイトルクラスプロパティ。
    • @Componentテンプレート<h1>要素。タイトルへのバインディングが含まれています。
  • 見出しのすぐ下のアプリテンプレートに、<my-heroes>要素を追加すると、まだヒーローが表示されます。
  • AppModuleの宣言配列にHeroesComponentを追加すると、Angularは、 <my-heroes>タグを認識します。
  • 他のすべてのビューでHeroServiceが必要になるため、HeroServiceAppModuleのプロバイダ配列に追加します。
  • HeroServiceプロバイダが昇格されてから、HeroServiceプロバイダ配列から、HeroServiceを削除します。
  • AppComponentのサポートするインポートステートメントを追加します。

最初のドラフトは次のようになります。

src/app/app.components.ts(v1)

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <my-heroes></my-heroes>
  `
})
export class AppComponent {
  title = 'Tour of Heroes';
}

src/app/app.module.ts(v1)

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 {
}

アプリはまだ実行され、英雄を表示します。

Adding routing

自動的に表示するのではなく、ユーザーがボタンをクリックした後にヒーローが表示されるはずです。

言い換えれば、ユーザーはヒーローのリストにナビゲートできる必要があります。

ナビゲーションを有効にするには、Angular routerを使用します。

Angular routerは、RouterModuleと呼ばれる外部のオプションのAngular NgModuleです。

ルータは、複数の提供サービス(RouterModule)、複数のディレクティブ(RouterOutletRouterLinkRouterLinkActive)と構成(Routes)の組み合わせです。

最初に経路を設定します。

<base href>

index.htmlを開き、<head>セクションの上部に<base href = “…”>要素(またはこの要素を動的に設定するスクリプト)があることを確認します。

src/index.html (base-href)

<head>
  <base href="/">

BASE HREF IS ESSENTIAL

“““
詳細については、Routing and Navigationページの「Set the base href」セクションを参照してください。
“““

Configure routes

アプリルートの設定ファイルを作成します。

ルートは、ユーザーがリンクをクリックするか、URLをブラウザのアドレスバーに貼り付けるときに表示するビューをルーターに指示します。

最初のルートを、ヒーローズコンポーネントへのルートとして定義します。

src/app/app.module.ts (heroes route)

import { RouterModule }   from '@angular/router';

RouterModule.forRoot([
  {
    path: 'heroes',
    component: HeroesComponent
  }
])

ルートは、ルート定義の配列です。

このルート定義には、次の部分があります。

・ パス: ルータは、ブラウザのアドレスバー(ヒーロー)のURLへのこのルートのパスを照合します。

・ コンポーネント: このルート(HeroesComponent)にナビゲートする際にルータが作成するコンポーネント。

“““
Routing&Navigation」ページで、「Routes」で経路を定義する方法の詳細を読んでください。
“““

Make the router available

RouterModuleをインポートし、AppModule imports配列に追加します。

src/app/app.module.ts (app routing)

import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';
import { RouterModule }   from '@angular/router';

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,
    RouterModule.forRoot([
      {
        path: 'heroes',
        component: HeroesComponent
      }
    ])
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}

“““
設定されたルーターが、アプリのルートに用意されているため、forRoot()メソッドが呼び出されます。

forRoot()メソッドは、ルーティングに必要なルータサービスプロバイダとディレクティブを提供し、現在のブラウザのURLに基​​づいて初期ナビゲーションを実行します。
“““

Router outlet

パスの/ heroesをURLの最後にあるブラウザのアドレスバーに貼り付けると、ルータはそれをヒーロールートに一致させ、HeroesComponentを表示する必要があります。

ただし、ルータにコンポーネントの表示先を伝える必要があります。

これを行うには、テンプレートの最後に<router-outlet>要素を追加します。

RouterOutletは、RouterModuleによって提供されるディレクティブの1つです。

ユーザーがアプリをナビゲートすると、ルータは<router-outlet>のすぐ下にある各コンポーネントを表示します。

Router links

ユーザーは、アドレスバーにルートURLを貼り付ける必要はありません。

代わりに、テンプレートにアンカータグを追加します。クリックすると、HeroesComponentへのナビゲーションがトリガーされます。

改訂されたテンプレートは次のようになります。

src/app/app.component.ts (template-v2)

template: `
   <h1>{{title}}</h1>
   <a routerLink="/heroes">Heroes</a>
   <router-outlet></router-outlet>
 `

アンカータグ内のrouterLinkバインディングに注意してください。

RouterLinkディレクティブ(別のRouterModuleディレクティブ)は、ユーザがリンクをクリックしたときにどこをナビゲートするかをルータに通知する文字列にバインドされています。

リンクは動的ではないので、ルーティング命令は、経路パスへの1回のバインディングで定義される。

ルート設定を振り返ると、 ‘/ heroes’が、HeroesComponentへのルートのパスであることを確認できます。

ダイナミックルータリンクとリンクパラメータ配列の詳細については、Routing&NavigationページのAppendix:Link Parameters Arrayセクションを参照してください。

ブラウザを更新します。

ブラウザには、アプリのタイトルとヒーローのリンクが表示されますが、ヒーローのリストは表示されません。

src/app/app.component.ts (v2)

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
     <h1>{{title}}</h1>
     <a routerLink="/heroes">Heroes</a>
     <router-outlet></router-outlet>
   `
})
export class AppComponent {
  title = 'Tour of Heroes';
}

AppComponentはルーターに接続され、ルーティングされたビューを表示します。

このため、このコンポーネントタイプを他の種類のコンポーネントと区別するために、このコンポーネントタイプをルーターコンポーネントと呼びます。

Add a dashboard

ルーティングは、複数のビューが存在する場合にのみ意味があります。

別のビューを追加するには、プレースホルダ DashboardComponentを作成します。

このプレースホルダは、ユーザーがナビゲートできる場所です。

src/app/dashboard.component.ts (v1)

import { Component } from '@angular/core';

@Component({
  selector: 'my-dashboard',
  template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent { }

後でこのコンポーネントをより便利にするでしょう。

Configure the dashboard route

ダッシュボードにナビゲートするように、app.module.tsを教えるには、ダッシュボードコンポーネントをインポートし、次のルート定義をルート定義配列に追加します。

src/app/app.module.ts (Dashboard route)

{
  path: 'dashboard',
  component: DashboardComponent
},

また、DashboardComponentをインポートしてAppModuleの宣言に追加します。

src/app/app.module.ts (dashboard)

declarations: [
  AppComponent,
  DashboardComponent,
  HeroDetailComponent,
  HeroesComponent
],
Add a redirect route

アプリが起動すると、ダッシュボードが表示され、ブラウザのアドレスバーに/ダッシュボードのURLが表示されます。

現在、ブラウザはアドレスバーに/で起動します。

これを実現するには、リダイレクトルートを使用します。

ルート定義の配列に以下を追加します。

src/app/app.module.ts (redirect)

{
  path: '',
  redirectTo: '/dashboard',
  pathMatch: 'full'
},

“““
Routing & Navigation ページの Redirecting routes セクションで、リダイレクトの詳細を確認します。
“““

Add navigation to the template

ヒーローズリンクのすぐ上にある、テンプレートへのダッシュボードナビゲーションリンクを追加します。

src/app/app.component.ts (template-v3)

template: `
   <h1>{{title}}</h1>
   <nav>
     <a routerLink="/dashboard">Dashboard</a>
     <a routerLink="/heroes">Heroes</a>
   </nav>
   <router-outlet></router-outlet>
 `

“““
<nav>タグはまだ何もしませんが、後でリンクをスタイルするときに便利です。
“““

ブラウザで、アプリケーションルート(/)に移動してリロードします。

アプリはダッシュボードを表示し、ダッシュボードとヒーローの間をナビゲートすることができます。

Add heroes to the dashboard

ダッシュボードをもっと面白くするために、上位4人のヒーローを一目で表示します。

テンプレートのメタデータを新しいテンプレートファイルを指すtemplateUrlプロパティに置き換えます。

src/app/dashboard.component.ts (metadata)

@Component({
  selector: 'my-dashboard',
  templateUrl: './dashboard.component.html',
})

このコンテンツを含むファイルを作成します。

src/app/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>

* ngForは再びヒーローのリストを反復してその名前を表示するために使用されます。余分な<div>要素は後でスタイリングに役立ちます。

Sharing the HeroService

コンポーネントのヒーロー配列を読み込むには、HeroServiceを再利用できます。

以前は、HeroServiceHeroesComponentのプロバイダ配列から削除し、それをAppModuleのプロバイダ配列に追加しました。

この動きにより、アプリケーションのすべてのコンポーネントで使用可能な、シングルトンのHeroServiceインスタンスが作成されました。

AngularはHeroServiceを注入し、DashboardComponentで使用することができます。

Get heroes

dashboard.component.tsに、次のimport文を追加します。

src/app/dashboard.component.ts (imports)

import { Component, OnInit } from '@angular/core';

import { Hero } from './hero';
import { HeroService } from './hero.service';

次のように、DashboardComponentクラスを作成します。

src/app/dashboard.component.ts (class)

export class DashboardComponent implements OnInit {

  heroes: Hero[] = [];

  constructor(private heroService: HeroService) { }

  ngOnInit(): void {
    this.heroService.getHeroes()
      .then(heroes => this.heroes = heroes.slice(1, 5));
  }
}

この種のロジックは、HeroesComponentでも使用されています。

・ ヒーロー配列プロパティを定義します。

・ コンストラクタにHeroServiceを挿入し、それをプライベートなheroServiceフィールドに保持します。

・ サービスを呼び出して、Angular ngOnInitライフサイクルフックの中のヒーローを取得します。

このダッシュボードでは、Array.sliceメソッドで4つのヒーロー(2位、3位、4位、5位)を指定します。

ブラウザを更新して、新しいダッシュボードに4つのヒーロー名を表示します。

Navigating to hero details

選択されたヒーローの詳細が、HeroesComponentの下部に表示されますが、ユーザーは次の追加の方法でHeroDetailComponentにナビゲートすることができます。

・ ダッシュボードから選択されたヒーローまで。

・ ヒーローリストから選ばれたヒーローまで。

・ ブラウザのアドレスバーに貼り付けられた”deep link”URLから。

Routing to a hero detail

app.module.ts内の HeroDetailComponentへのルートを追加できます。

他のルートが設定されています。

HeroDetailComponentに、どのヒーローを表示する必要があるかという点で、新しいルートは珍しいです。

あなたは、HeroesComponentまたは、DashboardComponentに何も言わせる必要はありませんでした。

現在、親のHeroesComponentは、コンポーネントのheroプロパティを次のようなバインディングを持つheroオブジェクトに設定します。

<hero-detail [hero]="selectedHero"></hero-detail>

しかし、このバインディングはどのルーティングシナリオでも機能しません。

Parameterized route

ヒーローのIDをURLに追加することができます。 idが11のヒーローにルーティングすると、次のようなURLが表示されます。

/detail/11

URLの/ detail /部分は定数です。末尾の数値は英雄から英雄に変わります。

ヒーローのIDを表すパラメータ(またはトークン)を使用して、ルートの可変部分を表現する必要があります。

Configure a route with a parameter

次のルート定義を使用します。

src/app/app.module.ts (hero detail)

{
  path: 'detail/:id',
  component: HeroDetailComponent
},

パスのコロン(:)は、次のことを示します。

idは、HeroDetailComponentにナビゲートするときに、特定のヒーローIDのプレースホルダです。

あなたはアプリのルートを終えました。

特定のヒーローを表示するためにナビゲーションリンクをクリックしないため、テンプレートに’Hero Detail’リンクを追加しませんでした。

名前がダッシュボードに表示されるか英雄リストに表示されるかにかかわらず、ヒーロー名がクリックされます。

HeroDetailComponentが改訂され、ナビゲートできる状態になるまで、ヒーローのクリックを追加する必要はありません。

Revise the HeroDetailComponent

HeroDetailComponentの実装は次のとおりです。

src/app/hero-detail.component.ts (current)

import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div>
        <label>id: </label>{{hero.id}}
      </div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})
export class HeroDetailComponent {
  @Input() hero: Hero;
}

テンプレートは変更されません。

ヒーローの名前も同じように表示されます。

大きな変化は、あなたが英雄の名前を得る方法によって引き起こされます。

あなたはもはや親コンポーネントのプロパティバインディングで主人公を受け取ることはありません。

新しいHeroDetailComponentは、ActivatedRouteサービスのObservableパラメータからidパラメータを取得し、HeroServiceを使用してそのIDを持つヒーローを取得する必要があります。

次のインポートを追加します。

src/app/hero-detail.component (added-imports)

// 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';

ActivatedRoute、HeroService、およびLocationサービスをコンストラクタに挿入し、その値をプライベートフィールドに保存します。

src/app/hero-detail.component.ts (constructor)

constructor(
  private heroService: HeroService,
  private route: ActivatedRoute,
  private location: Location
) {}

後で経路パラメータObservableで、使用するswitchMap演算子をインポートします。

src/app/hero-detail.component.ts (switchMap import)

import 'rxjs/add/operator/switchMap';

クラスにOnInitインターフェイスを実装するように指示します。

src/app/hero-detail.component.ts (implement)

export class HeroDetailComponent implements OnInit {

ngOnInitライフサイクルフックの中で、Observableというパラメータを使用して、ActivatedRouteサービスからidパラメータ値を抽出し、HeroServiceを使用してそのIDを持つヒーローを取得します。

src/app/hero-detail.component.ts (ngOnInit)

ngOnInit(): void {
  this.route.params
    .switchMap((params: Params) => this.heroService.getHero(+params['id']))
    .subscribe(hero => this.hero = hero);
}

switchMapオペレータは、ObservableルートパラメータのIDをHeroService.getHero()メソッドの結果である新しいObservableにマップします。

getHero要求が処理されている間にユーザーがこのコンポーネントに再度移動した場合、switchMapは古い要求を取り消し、HeroService.getHero()を再度呼び出します。

ヒーローidは数字です。

ルートパラメータは常に文字列です。

したがって、ルートパラメータの値は、JavaScript(+)演算子を使用して数値に変換されます。

“““
退会する必要がありますか?

Routing & Navigation ページのthe one-stop-shop for route information セクションにあるActivatedRouteで説明したように、ルーターは提供するオブザーバブルを管理し、サブスクリプションをローカライズします。

サブスクリプションは、コンポーネントが破棄されたときにクリーンアップされ、メモリーリークを防ぎます。

したがって、Observableというパラメーターparamsからの登録を解除する必要はありません。
“““

Add HeroService.getHero

前のコードスニペットでは、HeroServiceにはgetHero()メソッドがありません。

この問題を解決するには、HeroServiceを開き、getHeroesのヒーローリストをidでフィルタリングするgetHero()メソッドを追加します。

src/app/hero.service.ts (getHero)

getHero(id: number): Promise<Hero> {
  return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
}
Find the way back

ユーザーは、HeroDetailComponentにナビゲートするいくつかの方法を持っています。

別の場所に移動するには、AppComponentの2つのリンクのいずれかをクリックするか、ブラウザの戻るボタンをクリックします。

今度は、以前に挿入したLocationサービスを使用して、ブラウザのヒストリスタックの1ステップ前にナビゲートするgoBack()メソッドという3つ目のオプションを追加します。

src/app/hero-detail.component.ts (goBack)

goBack(): void {
  this.location.back();
}

あまりにも遠くに戻ると、ユーザーはアプリから出る可能性があります。

“““
実際のアプリケーションでは、この問題をCanDeactivateガードで防ぐことができます。 CanDeactivateページで詳しく読む。
“““

このメソッドは、コンポーネントテンプレートに追加する「戻る」ボタンにバインドされたイベントで結ばれます。

<button (click)="goBack()">Back</button>

テンプレートを、hero-detail.component.htmlという独自のファイルに移行します。

src/app/hero-detail.component.html

<div *ngIf="hero">
  <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>
</div>

コンポーネントのメタデータを、作成したばかりのテンプレートファイルを指すtemplateUrlで更新します。

src/app/hero-detail.component.ts (metadata)

@Component({
  selector: 'hero-detail',
  templateUrl: './hero-detail.component.html',
})

ブラウザを更新して結果を確認してください。

Select a dashboard hero

ユーザーがダッシュボードでヒーローを選択すると、アプリはHeroDetailComponentに移動して、選択したヒーローを表示および編集します。

ダッシュボードヒーローはボタンのようなブロックとして表示されますが、アンカータグのように動作するはずです。

ヒーローブロックにカーソルを合わせると、ターゲットURLがブラウザのステータスバーに表示され、ユーザーはリンクをコピーするか、新しいタブでヒーローの詳細ビューを開くことができます。

この効果を得るには、dashboard.component.htmlを再度開き、繰り返し<div * ngFor …>タグを<a>タグに置き換えます。

開始<a>タグを次のように変更します。

src/app/dashboard.component.html (repeated <a> tag)

<a *ngFor="let hero of heroes"  [routerLink]="['/detail', hero.id]"  class="col-1-4">

[routerLink]バインディングに注目してください。

このページの Router linksセクションで説明したように、AppComponentテンプレートの最上位のナビゲーションでは、宛先リンクの固定パス”/dashboard”と”/heroes”に設定されたルータリンクがあります。

今回は、リンクパラメータ配列を含む式にバインドしています。

配列には、目的地ルートのパスと現在のヒーローのIDの値に設定されたルートパラメータの2つの要素があります。

2つの配列項目は、前にapp.module.tsに追加したパラメータ化されたヒーロー詳細ルート定義のpathと:idトークンに揃えられています。

src/app/app.module.ts (hero detail)

{
  path: 'detail/:id',
  component: HeroDetailComponent
},

ブラウザを更新し、ダッシュボードからヒーローを選択します。

アプリはそのヒーローの詳細にナビゲートします。

Refactor routes to a Routing Module

約20行のAppModuleが4つのルートを構成するために使用されています。

ほとんどのアプリケーションにはさらに多くのルートがあり、不要なナビゲーションや不正なナビゲーションから保護するためのガードサービスが追加されています。 (Routing&NavigationページRoute Guardsセクションを参照してください。)ルーティングの考慮事項は、このモジュールをすぐに支配し、Angularコンパイラのアプリケーション全体に関する重要な事実を確立することを主目的としています。

ルーティング設定を独自のクラスにリファクタリングすることをお勧めします。

現在のRouterModule.forRoot()は、Angular ModuleWithProvidersを生成します。

ルーティング専用のクラスはルーティングモジュールでなければなりません。

詳細は、Routing&NavigationページMilestone#2:Routing Moduleセクションを参照してください。

規則によって、ルーティングモジュール名には「ルーティング」という単語が含まれ、ナビゲートされるコンポーネントを宣言するモジュールの名前と揃えられます。

app-routing.module.tsファイルを、app.module.tsの兄弟として作成します。

AppModuleクラスから抽出した次の内容を与えます。

src/app/app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';
const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard',  component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes',     component: HeroesComponent }
];
@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

ルーティングモジュールの典型的な例を次に示します。

・ ルーティングモジュールは、ルートを変数にプルします。

・ この変数は、将来モジュールをエクスポートする場合のルーティングモジュールパターンを明確にします。

・ ルーティングモジュールは、インポートにRouterModule.forRoot(ルート)を追加します。

・ ルーティングモジュールは、RouterModuleをエクスポートに追加して、コンパニオンモジュール内のコンポーネントがRouterLink

RouterOutletなどのルータ宣言にアクセスできるようにします。

・ 宣言はありません。宣言は、コンパニオンモジュールの責任です。

・ ガードサービスをお持ちの場合、ルーティングモジュールはモジュールプロバイダーを追加します。 (この例では何もありません。)

Update AppModule

ppModuleからルーティング設定を削除し、AppRoutingModuleをインポートします。

ESのインポートステートメントを使用して、NgModule.importsリストに追加します。

リファクタリング前の状態と比較して、改訂されたAppModuleがあります:

src/app/app.module.ts(after)

import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';
import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroesComponent }      from './heroes.component';
import { HeroService }          from './hero.service';
import { AppRoutingModule }     from './app-routing.module';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

src/app/app.module.ts(before)

import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';
import { RouterModule }   from '@angular/router';
import { AppComponent }        from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { DashboardComponent }  from './dashboard.component';
import { HeroesComponent }     from './heroes.component';
import { HeroService }         from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot([
      {
        path: '',
        redirectTo: '/dashboard',
        pathMatch: 'full'
      },
      {
        path: 'dashboard',
        component: DashboardComponent
      },
      {
        path: 'detail/:id',
        component: HeroDetailComponent
      },
      {
        path: 'heroes',
        component: HeroesComponent
      }
    ])
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}

AppModuleの改訂版と簡略版は、アプリの重要な部分を特定することに重点を置いています。

Select a hero in the HeroesComponent

HeroesComponentでは、現在のテンプレートは、下の選択されたヒーローの一番上にヒーローのリストと詳細が表示された”master/detail” スタイルを表示します。

template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <hero-detail [hero]="selectedHero"></hero-detail>
`,

上部の<h1>を削除します。

<hero-detail>タグでテンプレートの最後の行を削除します。

ここでは完全なHeroDetailComponentは表示されません。

代わりに、ヒーローの詳細を独自のページに表示し、ダッシュボードで行ったようにヒーローの詳細を表示します。

ただし、ユーザーがリストからヒーローを選択すると、詳細ページには移動しません。

代わりに、このページのミニディテールが表示され、ボタンをクリックして完全な詳細ページに移動する必要があります。

Add the mini detail

<hero-detail>が使われていたテンプレートの一番下に次のHTMLフラグメントを追加します。

src/app/heroes.component.ts (mini-detail)

<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>

ヒーローをクリックすると、ヒーローリストの下に次のようなものが表示されます。

Format with the uppercase pipe

ヒーローの名前は、パイプ演算子(|)の直後の補間バインディングに含まれる大文字のパイプのため、大文字で表示されます。

{{selectedHero.name | uppercase}} is my hero

パイプは、文字列、通貨金額、日付などの表示データを書式設定するのに適しています。

いくつかのパイプが付いている、Angular shipsはあなた自身で書くことができます。

“““
Read more about pipes on the Pipes page.
“““

・ コンテンツをコンポーネントファイルから移動する

ユーザーが「詳細を表示」ボタンをクリックしたときに HeroDetailComponentへのナビゲーションをサポートするために、コンポーネントクラスを更新する必要があります。

コンポーネントファイルが大きいです。

HTMLやCSSのノイズの中でコンポーネントのロジックを見つけるのは難しいです。

変更を加える前に、テンプレートとスタイルをそれぞれのファイルに移行してください。

まず、テンプレートのコンテンツを heroes.component.tsから新しい heroes.component.htmlファイルに移動します。バッククックをコピーしないでください。

heroes.component.tsに関しては、あなたはすぐにそれに戻ってきます。

次に、スタイルの内容を新しい heroes.component.cssファイルに移動します。

2つの新しいファイルは次のようになります。

src/app/heroes.component.html

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>

src/app/heroes.component.css

.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;
}

次に、heroes.component.tsのコンポーネントメタデータに戻って、テンプレートとスタイルを削除し、それぞれtemplateUrlとstyleUrlsに置き換えます。

新しいファイルを参照するようにプロパティを設定します。

src/app/heroes.component.ts (revised metadata)

@Component({
  selector: 'my-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: [ './heroes.component.css' ]
})

“““
styleUrlsプロパティは、スタイルファイル名の配列です(パスを使用)。必要に応じて、異なる場所から複数のスタイルファイルをリストすることができます。
“““

Update the HeroesComponent class

HeroesComponentは、ボタンクリックに応答してHeroesDetailComponentにナビゲートします。

ボタンのclickイベントは、どこに行くべきかをルータに伝えることによって、命令的にナビゲートするgotoDetail()メソッドにバインドされています。

このアプローチでは、コンポーネントクラスに以下の変更が必要です。

Angular router ライブラリからルータをインポートします。

HeroServiceと共にルータをコンストラクタに注入します。

router.navigate()メソッドを呼び出して、gotoDetail()を実装します。

src/app/heroes.component.ts (gotoDetail)

gotoDetail(): void {
  this.router.navigate(['/detail', this.selectedHero.id]);
}

DashboardComponentの[routerLink]バインディングと同じように、2要素のリンクパラメータ配列(パスとルートパラメータ)をrouter.navigate()メソッドに渡していることに注意してください。

改訂された HeroesComponentクラスがあります:

src/app/heroes.component.ts (class)

export class HeroesComponent implements OnInit {
  heroes: Hero[];
  selectedHero: Hero;

  constructor(
    private router: Router,
    private heroService: HeroService) { }

  getHeroes(): void {
    this.heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

  ngOnInit(): void {
    this.getHeroes();
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  gotoDetail(): void {
    this.router.navigate(['/detail', this.selectedHero.id]);
  }
}

ブラウザを更新して、クリックしてください。

ユーザーは、ダッシュボードからヒーローの詳細まで、ヒーローリストからミニディテール、ヒーローの詳細まで、そしてヒーローに再び戻って、アプリケーションの周りをナビゲートすることができます。

このページを推進したすべてのナビゲーション要件を満たしています。

Style the app

アプリは機能的ですが、スタイリングが必要です。

ダッシュボードヒーローは長方形の列に表示されます。

レスポンシブデザインのためのシンプルなメディアクエリを含む、この目的のために約60行のCSSを受け取りました。

あなたが今知っているように、CSSをコンポーネントスタイルのメタデータに追加すると、コンポーネントロジックが不明瞭になります。

代わりに、別の* .cssファイルでCSSを編集します。

dashboard.component.cssファイルをappフォルダに追加し、コンポーネントメタデータのstyleUrls配列プロパティでそのファイルを次のように参照します。

src/app/dashboard.component.ts (styleUrls)

styleUrls: [ './dashboard.component.css' ]
Add stylish hero details

HeroDetailComponent専用のCSSスタイルも用意されています。

hero-detail.component.cssをappフォルダに追加し、DashboardComponentの場合と同様にstyleUrls配列内のそのファイルを参照します。

また、hero-detail.component.tsでは、ヒーロープロパティ@Inputデコレータとそのインポートを削除します。

ここに、コンポーネントのCSSファイルのコンテンツがあります。

src/app/hero-detail.component.css

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;
}

src/app/dashboard.component.css

[class*='col-'] {
  float: left;
  padding-right: 20px;
  padding-bottom: 20px;
}
[class*='col-']:last-of-type {
  padding-right: 0;
}
a {
  text-decoration: none;
}
*, *:after, *:before {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
h3 {
  text-align: center; margin-bottom: 0;
}
h4 {
  position: relative;
}
.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;
}
.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;
  }
}
Style the navigation links

提供されたCSSは、AppComponentのナビゲーションリンクを選択可能なボタンのように見せます。

これらのリンクは、<nav>タグで囲みます。

app.component.cssファイルを、次の内容のappフォルダに追加します。

src/app/app.component.css (navigation styles)

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.active {
  color: #039be5;
}

“““
The routerLinkActive directive

Angular router は、routerLinkActive指令を提供します。

この指令を使用して、ルートがアクティブなルートと一致するHTMLナビゲーション要素にクラスを追加できます。

あなたがしなければならないことは、そのスタイルを定義することだけです。

src/app/app.component.ts (active router links)

template: `
  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
    <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
`,

“““

このCSSファイルを参照する、styleUrlsプロパティを次のように追加します。

src/app/app.component.ts (styleUrls)

styleUrls: ['./app.component.css'],
Global application styles

コンポーネントにスタイルを追加すると、コンポーネントが必要とするすべてのもの、つまりHTML、CSS、コードを1つの便利な場所に保存できます。

すべてのパッケージをパッケージ化し、コンポーネントを別の場所で再利用するのは簡単です。

また、コンポーネントの外にあるアプリケーションレベルでスタイルを作成することもできます。

デザイナーは、アプリ全体の要素に適用するための基本的なスタイルをいくつか用意しました。

これらは、セットアップ中に以前にインストールした一連のマスタースタイルに対応しています。

ここに抜粋があります:

src/styles.css (excerpt)

/* Master Styles */
h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
h2, h3 {
  color: #444;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: lighter;
}
body {
  margin: 2em;
}
body, input, button {
  color: #888;
  font-family: Cambria, Georgia;
}
/* . . . */
/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}

styles.cssファイルを作成します。

ファイルにここで提供されているマスタースタイルが含まれていることを確認します。

また、このスタイルシートを参照するために、index.htmlを編集してください。

src/index.html (link ref)

<link rel="stylesheet" href="styles.css">

今、アプリを見てください。

ダッシュボード、ヒーロー、ナビゲーションリンクはスタイリングされています。

Application structure and code

このページの live example / downloadable example のサンプルソースコードを確認してください。

次の構造を持っていることを確認します。

angular-tour-of-heroes
├──src
│ ├──app
│ │  ├──app.component.css
│ │  ├──app.component.ts
│ │  ├──app-routing.module.ts
│ │  ├──dashboard.component.css
│ │  ├──dashboard.component.html
│ │  ├──dashboard.component.ts
│ │  ├──hero.service.ts
│ │  ├──hero.ts
│ │  ├──hero-detail.component.html
│ │  ├──hero-detail.component.css
│ │  ├──hero-detail.component.ts
│ │  ├──heroes.component.css
│ │  ├──heroes.component.html
│ │  ├──heroes.component.ts
│ │  └──mock-heroes.ts
│ ├──main.ts
│ ├──index.html
│ ├──styles.css
│ ├──systemjs.config.js
│ └──tsconfig.json
├──node_modules ...
└──package.json

The road you’ve travelled

このページであなたが達成したことは次のとおりです。

・ さまざまなコンポーネント間を移動するために、 Angular routerを追加しました。

・ ナビゲーションメニュー項目を表すためのルーターリンクを作成する方法を学びました。

・ ルータリンクパラメータを使用して、ユーザが選択したヒーローの詳細に移動しました。

・ あなたは、複数のコンポーネント間で、HeroServiceを共有しました。

・ HTMLとCSSをコンポーネントファイルから自分のファイルに移動しました。

・ データをフォーマットするために大文字のパイプを追加しました。

あなたのアプリはこの live example / downloadable example.のように見えるはずです。

The road ahead

あなたは、アプリケーションを構築するために必要な基盤の多くを持っています。

あなたはまだ重要な部分を欠いています。 それは、remote data accessです。

次のページでは、httpを使用してサーバーから取得したデータで、模擬データを置き換えます。

Next Step

Angular2の始め方。Angular2 公式チュートリアル – HTTP(簡単な和訳)

  • RSSを登録する

  • follow us in feedly

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、港区六本木で活動中。

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。