Angular Materialで<mat-icon>を使う

この記事は、[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 11日目の記事です。

## 概要
`<mat-icon>`は[Angular Material](https://material.angular.io/)のコンポーネントの1つで、豊富なマテリアルアイコンをアプリケーション内で簡単に使うことができます。  
本記事では、Angular Materialインストール時に読み込まれる[Material Icons](https://material.io/tools/icons/)を用いた`<mat-icon>`の使用方法と、登録したSVGアイコンを`<mat-icon>`で使用する方法について説明します。  
本記事のサンプルコードは既にAngular CLIでプロジェクトを生成している場合を想定しています。

## `<mat-icon>`でMaterial Iconsを表示する
前述の通り、`<mat-icon>`とはベクター形式のアイコンを扱うことができる、Angular Materialのコンポーネントになります。特徴としては、アイコン名とSVGアイコンのURLを結びつけたものを、HTML文字列やCSSのクラス名のエイリアスとして登録することができます。また、`<mat-icon>`では、SVGアイコンを登録するだけでなく、Material Iconsを簡単に利用することができます。  
まずは、`<mat-icon>`を利用するために、Angular Materialをインストールします。

### Angular Materialをインストールする
下記コマンドでAngular Materialをインストールします。

> ng add @angular/material

インストールが完了すると、`src/index.html`にGoogle APIsから配信されているMaterial Iconsのスタイルシートが読み込まれているようになります。

```src/index.html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
```

`<mat-icon>`を使用するためには、`app.module.ts`に、`MatIconModule`をインポートします。

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

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+ import { MatIconModule } from '@angular/material/icon';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
+    MatIconModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

これでアプリケーション内でMaterial Iconsを使用できる準備ができました。

### Material Iconsを表示する
Material Iconsを表示するには、`<mat-icon></mat-icon>`タグ内に使用したいMaterial Iconsのアイコン名を記述します。下記はそのサンプルになります。

```src/app/app.component.html
<mat-icon>thumb_up</mat-icon>
```

先ほどのサンプルはブラウザで下記のように表示されます。
<img width="1440" alt="スクリーンショット 2018-12-10 10.49.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/132594/9675a23e-8f91-d14e-2b4f-0b52bf9bc24f.png">

合字と呼ばれる複数の文字を合成して一文字にしたもの[^1]を用いて、`thumb_up`というテキストからアイコンを表示させています。  

デフォルトカラーは`<mat-icon>`の`currentColor`プロパティによって制御されており、親要素の持つ`color`プロパティを継承します。`<mat-icon>`の`font-size`や`color`などのプロパティを変更することでアイコンのサイズや色を変更することも可能です。

### `<mat-icon>`で使いたいアイコンを探す
「Material Iconsとして使うことができる種類はどれくらいあるのか」、「アイコン名は何になるのか」といった場合には Material Designの[Material Icons ライブラリ](https://material.io/tools/icons/?style=baseline)を参照するとよいでしょう。
<img width="1440" alt="スクリーンショット 2018-12-07 20.17.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/132594/296c6196-eba2-bb95-b430-850d0ec0b609.png">
上記はMaterial iconsに存在するアイコンの一覧で900種以上のアイコンが登録されています。

## SVGアイコンを登録する
`<mat-icon>`では Material Icons以外にも、Angular Materialの`MatIconRegistry`サービスを用いることで、自分で準備したSVGアイコンをアプリケーション内で使用することができます。  
必要なモジュールを先ほどのプロジェクトにインポートしましょう。

### SVGファイルを格納する
Material Iconライブラリでは、全てのアイコンがSVGまたはPNG形式でファイルダウンロードすることができます。今回はSVGアイコン登録用にMaterial Iconライブラリから、ツートーンのfavoriteアイコンをダウンロードし、`src/assets`配下に格納します。SVGのファイル名は`twotone-favorite-24px.svg`とします。

### HttpClientModuleをインポート
登録するSVGアイコンのURLはXmlHttpRequestで取得されるため、AppModuleモジュールに`HttpClientModule`をインポートします。

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

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatIconModule } from '@angular/material/icon';
+ import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MatIconModule,
+    HttpClientModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

```

### コポーネントでSVGアイコンを登録する
SVGアイコンを利用したいコンポーネントで`DomSanitizer`と`MatIconRegistry`サービスを注入し、`MatIconRegistry.addSvgIcon`メソッドで任意のSVGアイコンを登録します。  
`DomSanitizer`サービスはXSSを防ぐために、`MatIconRegistry`に渡すSVGアイコンのURLや文字列に信頼済みマークを付与するために使用します。

```src/app/app.component.ts
import { Component } from '@angular/core';
+ import { DomSanitizer } from '@angular/platform-browser';
+ import { MatIconRegistry } from '@angular/material';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
+  constructor(
+    private domSanitizer: DomSanitizer,
+    private matIconRegistry: MatIconRegistry,
+  ) {
+    this.matIconRegistry.addSvgIcon(
+      'twotone-favorite',
+      this.domSanitizer.bypassSecurityTrustResourceUrl('src/assets/twotone-favorite-24px.svg'));+  }
}
```

上記の例では、`addSvgIcon`の第一引数に登録したアイコンを呼び出すための名前を設定し、第二引数に登録したいアイコンのURLを設定しています。

### 登録したSVGアイコンを使う

それでは先ほど登録したSVGアイコンをHTMLで呼び出してみましょう。`mat-icon`タグに`svgIcon`属性タグを付与して、先ほど設定したアイコン名を値として代入します。

```src/app/app.component.html
<mat-icon svgIcon="twotone-favorite"></mat-icon>
```

実行結果は下記のようになります。
<img width="1440" alt="スクリーンショット 2018-12-08 23.21.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/132594/0a15ecbc-8eda-c442-7c72-ae190d792979.png">

`<mat-icon>`はSVGアイコンを表示すると、`<mat-icon>`の子要素にSVG要素をインラインで展開します。
MatIconRegistryサービスを使うメリットとしては、複雑なパスを持ったSVGアイコンをコンポーネント化することで、コードの見通しが良くなり、リファクタリングしやすくなることでしょうか。

## まとめ
Angular Materialの`<mat-icon>`とMaterial Iconsを用いたアイコンフォントの使い方と、SVGアイコンを登録する方法を紹介しました。どちらも有効に活用することでアプリケーションをより洗練されたものにする手助けになるでしょう。

[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 12日目は、@thimi0412さんです。

## 参考文献
下記、執筆にあたり参考にさせて頂いた記事・サイトです。

[How to Use Custom SVG Icons in Angular Material](https://alligator.io/angular/custom-svg-icons-angular-material/)
[Angular Material](https://material.angular.io/)
[Material Design](https://material.io/)

[^1]: [Material Icons Guide](http://google.github.io/material-design-icons/#using-the-icons-in-html)