Angular入門 未経験から1ヶ月でサービス作れるようにする その3. ルーティング基礎

[前回](https://qiita.com/seteen/items/ef41c321bff47d66a443)は、

- ディレクトリ構造
- コンポーネントの基本的な変更の仕方

を学びました。 

本記事では、

- SCSSの有効化
  - CSSでは不便だと言う人向けに、SCSSを使うように設定を変更します。
- 新しくページを作って、ページ間の遷移を行う
  - 基本的なルーティングの設定方法
  - 新しいコンポーネントの作り方
  - ページ間のリンクを貼ってみる

をやっていきます。

## この記事のソースコード
この記事終了時のソースコードは、下記を参照してください。

[https://github.com/seteen/AngularGuides/tree/入門その03](https://github.com/seteen/AngularGuides/tree/%E5%85%A5%E9%96%80%E3%81%9D%E3%81%AE03)

## SCSSの有効化

まず Angular内で利用する CSS を SCSS に変更しましょう。
(CSSの方が好きだという方はこの手順はスキップして大丈夫です）

### グローバルスタイルの定義（style.css）のSCSSへの変更
まずは、Angularのグローバルなスタイル(このスタイルはすべてのコンポーネントに影響します)を定義している `src/style.css` を変更します。

まずは、ファイルをリネームします。

```
mv src/style.css src/style.scss
```

これはただのファイル名の変更なので、普通にGUIで変更しても問題ありません。

次に、 `angular.json` を変更します。

``` angular.json
{
  ...
  "projects": {
    "angular-sample": { # ここは別の名前でプロジェクトを作った人は別になります
      ...
      "architect": {
        ...
        "build": {
          ...
          "options": {
            ...
            "styles": [
              "style.css" # これを styles.scss に変更
            ]
          }
        },
        ...
        "test": {
          ...
          "options": {
            ...
            "styles": [
              "style.css" # これを styles.scss に変更
            ]
          }
        }
      }
    }
  }
}
```

上記のように変更することで、グローバルなスタイル定義が `src/styles.scss` に変更されます。

#### 実際のコミット
この変更の実際のコミットが下記なので、よくわからない、という人は参考にしてください。

https://github.com/seteen/AngularGuides/commit/9bc97d083aa73be61543753f0f7fb0cc185b5802

### プロジェクトのデフォルト設定をCSSからSCSSに変更
プロジェクトのデフォルト設定を SCSS に変更することで、後ほど出てくる コンポーネントを作成してくれるコマンドで自動生成されるスタイルファイルをCSSからSCSSに変更してくれます。

変更するのは、下記です。

``` angular.json
{
  ...
  "projects": {
    "angular-sample": { # ここは別の名前でプロジェクトを作った人は別になります
      ...
      "schematics": { # ここは空なので、下記を追加する
        "@schematics/angular:component": {
          "styleext": "scss"
        }
      }
    }
  }
}
```

#### 実際のコミット
この変更の実際のコミットが下記なので、よくわからない、という人は参考にしてください。

https://github.com/seteen/AngularGuides/commit/c3ca15153a7c81abceb5290d46b38a7a51cbdc47


## コンポーネントの作成

では、本題に入っていきましょう。

まずは、ページ（コンポーネント）を２つ作ります。

Angular CLI には、コンポーネントを作成するためのコマンドが用意されているので、使ってみましょう。

```
ng g component product/product-list
```

上記コマンドを実行してみると、下記のように表示されます。

```
# ng g component product/product-list
CREATE src/app/product/product-list/product-list.component.scss (0 bytes)
CREATE src/app/product/product-list/product-list.component.html (32 bytes)
CREATE src/app/product/product-list/product-list.component.spec.ts (671 bytes)
CREATE src/app/product/product-list/product-list.component.ts (297 bytes)
```

続けて、もう一つページを作成しましょう。

```
ng g component product/product-detail
```

上記コマンドを実行します。

```
# ng g component product/product-detail
CREATE src/app/product/product-detail/product-detail.component.scss (0 bytes)
CREATE src/app/product/product-detail/product-detail.component.html (34 bytes)
CREATE src/app/product/product-detail/product-detail.component.spec.ts (685 bytes)
CREATE src/app/product/product-detail/product-detail.component.ts (305 bytes)
UPDATE src/app/app.module.ts (554 bytes)
```

これにより、新しくディレクトリとファイルが作成されました。

![001.png](https://qiita-image-store.s3.amazonaws.com/0/67424/93e55d85-0436-8cb7-d3ad-3a14ede400ed.png)

> TIPS: ディレクトリ構造
今回のプロジェクトでは、 `src/app/product` というディレクトリに `product` 関連ページのコンポーネントを入れていきます。
Angularでは、ディレクトリ構造は自由に決められますが、関連するページをディレクトリにまとめるなどはやったほうが良いと思います。
参考↓
https://angular.io/guide/styleguide#style-04-06

### 実際のコミット
もし、CLIのコマンドがうまく動かない、という人がいたら、下記コミットを参考にしてファイルを作成してください。
特にコマンドによる作成の方が良いというわけではないです。自分でファイルを作成しても変化はありません。

https://github.com/seteen/AngularGuides/commit/1dc2fd557bac4baf0d76c4a4dbc5d0b96bef17fa

## ルーティングファイルの作成

次に、ルーティングファイルを作成します。
ルーティングファイルというのは、AngularにおけるURLとコンポーネントの対応を定義するファイルです。

おそらく、最初は各要素が何を意味しているかわからないと思いますが、まずは書いてみましょう。

``` src/app/app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
import { ProductListComponent } from './product/product-list/product-list.component';
import { ProductDetailComponent } from './product/product-detail/product-detail.component';

const routes: Routes = [
  { path: 'products', component: ProductListComponent },
  { path: 'products/:id', component: ProductDetailComponent },
  { path: '', redirectTo: '/products', pathMatch: 'prefix' },
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule {}
```

### 解説

解説と言いつつ、 `@NgModule` 以下の行は、とりあえずおまじないと思って無視しましょう。

いったんここでは「なるほどわからん」くらいに思っておいてください。

重要なのは、 `const routes: Routes` です。
ここで、ルーティングの定義を行っています。

```
{ path: 'products', component: ProductListComponent },
```

まず、これは、 `http://localhost:4200/products` の URL（サーバにあげればホスト名は変わります）を 先程作成した `ProductListComponent` にマッピングする、という意味です。
ここは非常にわかりやすいですね。

ただ、 **まだこの時点では実際にアクセスしてもマッピングされていない** ので試すのはもう少し待っておいてください。

```
{ path: 'products/:id', component: ProductDetailComponent },
```

次に、この行ですが、これは、 `http://localhost:4200/products/xxx (xxxは何でも良い）` を `ProductDetailComponent` にマッピングするという意味です。
上記の `:id` は、コンポーネントからパラメータとして取得できるので、その内容によってコンポーネントの状態を変更させたい場合に利用します。

`product` の `id` をURLから取得することで、最終的には API を叩いてその `id` の `product` の情報を取得して表示する、というコンポーネントにしたいと思っています。

```
{ path: '', redirectTo: '/products', pathMatch: 'prefix' },
```

最後にこの行ですが、上で説明した2つのURL以外のURLが指定されると、自動的に `/products` にリダイレクトさせるという意味です。

> TIPS: pathMatch とは
> `pathMatch` は、 `full` か `prefix` が指定できます。
>
>`full` の場合はURLが `path` と完全に一致した場合（今回の場合 >`http://localhost:4200/` ) にリダイレクトします。
>
>`prefix` の場合は、URLが `path` の値で始まる場合 (今回の場合は、>`http://localhost:4200/` で始まるすべてのURL。事前に定義されている `/products` >, `/products/:id` を除く) にリダイレクトします。

#### 実際のコミット
この変更の実際のコミットが下記なので、よくわからない、という人は参考にしてください。

https://github.com/seteen/AngularGuides/commit/fe4ac6c3cae9e44b2ff36a674c9e98c64b44b9a8

## ルーティングを有効な状態にする

現状では、まだルーティングは有効ではありません。

なぜなら、`app-routing.module.ts` はプロジェクト上に存在しているだけでどこからも参照されていないからです。

### app.module.tsから参照する

このプロジェクトのモジュール群を管理している `app.module.ts` に `app-routing.module.ts` を追加します。

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

import { AppComponent } from './app.component';
import { ProductListComponent } from './product/product-list/product-list.component';
import { ProductDetailComponent } from './product/product-detail/product-detail.component';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [
    AppComponent,
    ProductListComponent,
    ProductDetailComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule, // ここを追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

### app.component.tsを変更する

上記で、 `app.module.ts` から参照されました。

ただ、現状の `app.module.ts` は、 `app.component.ts(html,css)` の内容を表示しています。

このプロジェクトは `app.component.ts(html,css)` を表示する、というのがこのプロジェクトの起点になっています。
そのため、 `app.component.html` でルーティングされたコンポーネントを表示する、ということが必要になります。

ルーティングされているコンポーネントを表示するには `<router-outlet></router-outlet>` という要素を利用します。

`app.component.html` を書き換えます。

``` src/app/app.component.html
<router-outlet></router-outlet>
```

これにより、ルーティングされたコンポーネントが表示されるようになります。

```
ng serve
```

でプロジェクトを起動して確認してみましょう。

![002.png](https://qiita-image-store.s3.amazonaws.com/0/67424/8edafa8c-853a-37a1-d358-153fe62f79ad.png)

`http://localhost:4200/` にアクセスすると、上記のように自動で `http://localhost:4200/products` にリダイレクトされます。

表示されている内容も、 `ProductListComponent` のものとなっていることが確認できます。

また、  `http://localhost:4200/products/test` にアクセスしてみましょう。

![003.png](https://qiita-image-store.s3.amazonaws.com/0/67424/766a1a9e-e846-383e-71c7-8a52e4f8e495.png)


今度は `ProductDetailComponent` の内容が表示されることが確認できます。

#### 実際のコミット
https://github.com/seteen/AngularGuides/commit/cb2f9dc8a75b4063e3ebd2f2826eb4976da55a48

## ページ間のリンクを作ってみる

Angularの中で リンクを作ってみましょう。Angularのコンポーネント中では、 `<a>` 要素を用いてリンクを作らずに、別のやり方でリンクを作成します。
（ 実は `<a>` 要素も動きますが、非推奨です )


### 準備として、ボタンのCSSを用意

少し見栄えを良くするために、ボタンのCSSを作っておきます。 `styles.scss` を下記のように修正します。

``` src/style.scss
/* You can add global styles to this file, and also import other style files */
.button {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 35px;
  width: 135px;
  border-radius: 4px;
  background-color: #2196F3;
  color: #FFFFFF;
  font-size: 14px;
  line-height: 21px;
  cursor: pointer;

  &.black {
    background-color: #212121;
  }

  &.white {
    color: #212121;
    background-color: white;
    border: 1px solid #212121;
  }
}
```

### リンクを作成

では、まずは  `ProductDetailComponent` から `ProductListComponent` へのリンクを作成します。

``` src/app/product/product-detail/product-detail.component.html
<p>
  product-detail works!
</p>
<div class="button" routerLink="/products">
  商品一覧へ
</div>
```

追加した、項目の `routerLink` というのが、 Angular上でのルートの遷移を行うための属性になります。

とても簡単ですね。

なお、 その1 で紹介した `WebStorm` のプラグインを使えば、補完が効きます。便利ですね。

![004.png](https://qiita-image-store.s3.amazonaws.com/0/67424/51a25fb9-c33d-dc62-292e-62b315b24b1a.png)



次に、 `ProductListComponent` から `ProductDetailComponent` へのリンクを作成します。

今回は、別の指定の方法を行います。

``` src/app/product/product-list/product-list.component.html
<p>
  product-list works!
</p>
<div class="button" [routerLink]="['/products', 'test']">
  商品詳細へ
</div>
```

前回とは少し違う書き方をしていることに気づきましたか？

今回は、 `[routerLink]` という風に `routerLink` を `[]` で囲いました。

 この `[]` で囲った属性は、Angularでは要素に対して Javascript の要素を入力するという意味になります。

 `[routerLink]` は、配列を受け取ります。配列の各要素は、ルーティングの階層を表していて、これは、 `<div class="button" routerLink="/products/test">` と書いたときと全く同じ動きをします。

実際に起動してみましょう。

`http://localhost:4200/products` にアクセスすると、下記のような画面が表示されます。

![005.png](https://qiita-image-store.s3.amazonaws.com/0/67424/6a6a43b8-0178-1d81-f9ea-3bafb1fe363c.png)


ボタンを押すと、 `http://localhost:4200/products/test` に遷移します。

![006.png](https://qiita-image-store.s3.amazonaws.com/0/67424/0cbbdbed-fa24-7bc8-f108-8af8933dbc10.png)


#### ここまでのコミット

https://github.com/seteen/AngularGuides/commit/736cb5508c3a26e011442697e65a7398564ae0d2

## [routerLink] の使いどころ

`routerLink` と `[routerLink]` の2つのリンクの作り方を紹介しました。

同じなら、なぜこんな書き方をするの？と思った方も多いと思います。

今回は、 `/products/test` というすでに決まりきっている内容を入れましたが、例えばリンクの内容が Typescript 上で計算された値を入れたいときには、この書き方はとても有用になります。

少し `ProductListComponent` を変更してみます。

``` src/app/product/product-list/product-list.component.ts
import { Component, OnInit } from '@angular/core';

 @Component({
   selector: 'app-product-list',
   templateUrl: './product-list.component.html',
   styleUrls: ['./product-list.component.scss']
 })
 export class ProductListComponent implements OnInit {
   id = '100'; // ここを追加

   constructor() { }

   ngOnInit() {
   }

 }
```


``` src/app/product/product-list/product-list.component.html
<p>
  product-list works!
</p>
<div class="button" [routerLink]="['/products', id]">
  商品詳細へ
</div>
```

今回は、リンクの値を Typescript の変数から取得するようにしました。

このように、 Typescript で定義したり、計算したりしている値を使いたいときに、 `[routerLink]` の書き方は有効です。

ここでも、 `WebStorm` のプラグインを利用することで、補完が効きます。やっぱり便利ですね。

![007.png](https://qiita-image-store.s3.amazonaws.com/0/67424/77b5e755-6e51-33a9-c90f-934266051d21.png)


#### ここまでのコミット
https://github.com/seteen/AngularGuides/commit/c82b5f052714946d74eab091b0e37b693c94e3e3

## 終わりに

今回は、ルーティングの基礎を学びました。

まだおまじないのような部分があると思いますが、現状ではそこまで理解しなくても大丈夫なので、説明した部分だけをしっかり覚えておいてください。

次回は、Angularにおけるデータ管理の基礎、およびHTML内での分岐、ループについて解説していきます。


[Angular入門 未経験から1ヶ月でサービス作れるようにする その4. モデルとループと分岐と](https://qiita.com/seteen/items/445a550e27d9ff76eaec)

## 入門記事一覧
「Angular入門 未経験から1ヶ月でサービス作れるようにする」は、記事数が多いため、[まとめ記事](https://qiita.com/seteen/items/43908e33e08a39612a07) を作っています。
https://qiita.com/seteen/items/43908e33e08a39612a07