Angularでスライドショー(1) 〜Routerでページの切り替え〜

#はじめに

この記事では、Angularでスライドショーを作成する方法について説明します。
第1回目は、Router(ルーター)機能を使ったページの切り替えについてです。
(環境は、この記事の執筆時点の最新バージョンである Angular 4.3.4 です)

解説はいつものとおり「恋に落ちるコード.js」の絵子と樹里がお送りします。

##人物紹介

瀬尾絵子:「恋に落ちるコード.js」の登場人物。JavaScript勉強中の女子高生。
篠宮樹里: 同じく「恋に落ちるコード.js」の登場人物。JavaScriptに詳しい女子高生。

#目的

樹里「絵子、Angular CLIでスライドショーを作ってみよう」
絵子「相変わらず話が唐突だね。何でまた急に」
樹里「先日、同じくAngular CLIでRedmineのチケットの一覧を表示するWebアプリを作成したが」
絵子「うん。カンタンに出来るもんだなー、とビックリしちゃった」
樹里「Redmineで管理されているタスクは、結局のところRedmineにアクセスしないと確認できない。故に、常にRedmineを確認する習慣を付けないと見落としが発生する」
絵子「確かに。習慣を根付かせるまでは大変だよね」
樹里「そこで、APIからチケットの情報を取得してスライドショー形式で表示させれば、Redmineを開かずともタスクを確認することができる」
絵子「自然と目に入るもんね」
樹里「膨大なタスクの数にウンザリするも良し、いつまでも片付かない長寿タスクにウンザリするも良し」
絵子「何が良しなのかわからないけど、でも見える化って有効だよね」

#ひな形を作成する

樹里「ではさっそく作ってみるが……絵子がちゃんと理解出来るよう、段階を追って作っていくぞ。今日の目標は、手動でのページ切り替えだ」
絵子「お気づかいありがとう」
樹里「さて、アプリ名だが……絵子、アプリ名は`slideshow`と`Leg-Fetishism`のどっちがいい？」
絵子「ぜひ`slideshow`でお願いします」
樹里「そうか。では」

```
ng new slideshow --routing
```

絵子「ひな形を自動で作ってくれるコマンドだね……でも樹里、最後の`--routing`って何？」
樹里「このオプションを追加すると、ルーティング用のモジュールも同時に作ってくれる。そして、そのモジュールこそが重要であり、今回の主役だ」
絵子「へー。……で、ルーティングって何？毛づくろいだっけ？」
樹里「そりゃグルーミングだ。全然合ってないぞ……ざっくり言うと、『こういうURLの時はこのコンテンツを表示させるよ』というルールを定義することだ。『/page1』というURLならpage1の内容を表示する、と言った具合に」
絵子「あ、それならわかるよ。それを早く言ってよ」
樹里「ただ、ルーティングはAngularでは基本中の基本機能なので、優良な解説記事はここQiitaを始めいくらでも存在する。詳しいことはそちらを参照するように。今回は、あくまで次回以降の記事の準備だと思ってほしい」
絵子「筋道立てて説明しないとね。ルートだけにね」

#コンポーネントを追加する

樹里「では、ひな形が出来上がったところで、表示させるWebページのコンポーネントも追加しておこう。これもコマンドで簡単に出来る」

```
ng g component page1
ng g component page2
ng g component page3
```

樹里「gはgenerateの略で、生成するという意味だ。このコマンドを打つと、page1などのディレクトリが作られ、中に必要なファイルが生成される。とりあえず3ページ分作った」
絵子「おー、htmlとかcssとか色々できてる」
樹里「ファイルの生成は以上だ。次は、ひな形を修正していくぞ」

#ルーティング用モジュールを修正する

樹里「それでは今回の主役、ルーティング用モジュールを修正しよう。app-routing.module.tsを開いて次のように書き換える」

```js:app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
/* ↓(1) コンポーネントのインポート */
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';
/* ↑ */

const routes: Routes = [
/*
  {
    path: '',
    children: []
  }
*/
/* ↓(2) ルーティングの設定 */
  {
    path: 'page1',
    component: Page1Component,
  },
  {
    path: 'page2',
    component: Page2Component,
  },
  {
    path: 'page3',
    component: Page3Component
  }
/* ↑ */
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
```
##(1)コンポーネントのインポート

樹里「さっき作った3つのコンポーネントを読み込んでいるだけだ」
絵子「あなたの出番ですよー、って教えてあげているわけだね」

##(2)ルーティングの設定

樹里「ここでルーティングを設定してるわけだが」
絵子「思ってたよりシンプルだね。しかも、何を言わんとしてるのか何となくわかる。つまり、『page1』というURLにアクセスしたら『Page1Component』を表示させなさい、っていう風に指定してるんだよね？」
樹里「その通り。ただ、従来のWebページとは違い、URLの遷移によってページ全体が書き換わるわけではない。書き換わるのは、あくまでページの一部だけだ」
絵子「ふーん」

#メインコンポーネントを修正する

樹里「では、メインのコンポーネントであるapp.component.tsを見てみよう」

```js:app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}
```

絵子「……特に変わったところはなさそうだけど」
樹里「そう。このファイルは、今回は修正するところはない。その代わり、HTMLのテンプレートを次のように書き換える」

```html:app.component.html
<p>
  <a routerLink="/page1">page1</a>
  <a routerLink="/page2">page2</a>
  <a routerLink="/page3">page3</a>
</p>
<p>この下の部分が切り替わるよ！</p>
<router-outlet></router-outlet>
```

樹里「一番下の`<router-outlet></router-outlet>`の部分が、ルーティングによって書き換えられる部分だ。逆に言うと、それ以外の部分は一切変化しない」
絵子「なるほど」
樹里「で、リンク先に遷移するためには見慣れた`<a>`要素を使うのだが、`href`ならぬ`routerLink`ディレクティブというものを用いる」
絵子「なるほど」

#メインモジュールを確認する

樹里「では最後に、メインのモジュールであるapp.module.tsを見てみよう

```js:app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';

@NgModule({
  declarations: [
    AppComponent,
    Page1Component,
    Page2Component,
    Page3Component
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

絵子「あ、`Page1Component`とかがいつの間にか追加されてる」
樹里「そう、`ng g`コマンドでコンポーネントを追加すると、app.module.tsにも自動で追加してくれるのだ」
絵子「気が効くよねー」
樹里「なので、このモジュールも特に書き換えるところはない」

#動作確認

樹里「では、さっそく動かしてみよう」

```
ng s
```

絵子「コマンド短っ！」
樹里「`s`は`serve`の略だ。これだけでサーバーが起動する。では。さっそく http://localhost:4200/ にアクセスしよう」
絵子「リンクが3つ並んでるね。クリックすると……おー、『page1 works!』だって」
樹里「それはpage1.component.htmlの内容だな。他のリンクをクリックすると、当然他のページが表示される」
絵子「うんうん、ちゃんと切り替わってる」
樹里「アドレスバーを見ると、 http://localhost:4200/page1 と言うように、URLも切り替わってるのがわかるだろう。アドレスバーに直接URLを入力しても同じ結果になるぞ」
絵子「本当だ」
樹里「このように、『URLに応じて表示するコンポーネントを切り替える』というのは、Angularを使う上で非常に重要な機能なので、ぜひ覚えてほしい」
絵子「はーい」

#次回予告

次回は、タイマーによるページの自動遷移について解説します。