Angular Materialを試す

f:id:tworks55:20200510111756p:plain Angular Materialを試してみましたのでブログに残しておきます。

これは文字通りAngular向けのMaterial Design componentsで、Material Designに従ったButtonやCheckboxなどさまざまなコンポーネントを簡単に使うことができる仕組みです。






Material Design 自体にMaterial Themingという概念があり、全体の色などを簡単にカスタマイズできるようになっています。

Angular Materialはデフォルトで4つのテーマが選択できるようになっています。

  • deeppurple-amber.css
  • indigo-pink.css
  • pink-bluegrey.css
  • purple-green.css

トップページの右上のアイコンでテーマを変更し、雰囲気を見ることができます。 f:id:tworks55:20200510111332g:plain




以降では実際にいくつかのAngular Materialのコンポーネントを使ってみます。

既にAngularの開発環境は整っている前提です。

出来上がりはこのような感じです。

f:id:tworks55:20200510211940p:plain

Angularアプリケーションの作成

以下の流れでベースとなるアプリケーションを作成します。

# ng new my-app
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS

設定を聞かれますがひとまずEnterキーを押してデフォルト設定とします。

次はAngular Materialのパッケージを設定します。

# cd my-app
# ng add @angular/material
? Choose a prebuilt theme name, or "custom" for a custom theme: (Use arrow keys)
❯ Indigo/Pink        [ Preview: https://material.angular.io?theme=indigo-pink ] 
  Deep Purple/Amber  [ Preview: https://material.angular.io?theme=deeppurple-amber ] 
  Pink/Blue Grey     [ Preview: https://material.angular.io?theme=pink-bluegrey ] 
  Purple/Green       [ Preview: https://material.angular.io?theme=purple-green ] 
  Custom 

? Set up global Angular Material typography styles? No

? Set up browser animations for Angular Material? Yes

こちらも設定を聞かれますがひとまずEnterキーを押してデフォルト設定とします。




次にベースの上記で作成したアプリケーションの内容を変更していきます。だたし、BottomSheetの機能は別ファイルでコンポーネントを作成しています。

手間を省く為、すべてsrc/appディレクトリで完結させています。

既存ファイルの変更

app.component.css (追加のみ)

/* 元は何も無いので以下を追加 */
.example-button-row button,
.example-button-row a {
    margin: 8px;
}

mat-grid-tile {
    background: lightblue;
}

app.component.html (すべて入れ替え)

/* 元をすべて削除し以下を追加 */
<mat-grid-list cols="2" rowHeight="2:1">
    <mat-grid-tile>


        <div class="example-button-row">
            <button mat-raised-button>Basic</button>
            <button mat-raised-button color="primary">Primary</button>
            <button mat-raised-button color="accent">Accent</button>
            <button mat-raised-button color="warn">Warn</button>
            <button mat-raised-button disabled>Disabled</button>
            <a mat-raised-button href="https://www.google.com/" target="_blank">Link</a>
        </div>
    </mat-grid-tile>
    <mat-grid-tile>

        <div class="example-button-row">

            <p>You have received a file called "cat-picture.jpeg".</p>
            <button mat-raised-button (click)="openBottomSheet()">Open file</button>

        </div>

    </mat-grid-tile>
    <mat-grid-tile>
        <div class="example-container">
            <mat-form-field appearance="fill">
                <mat-label>Input</mat-label>
                <input matInput>
            </mat-form-field>
            <br>
            <mat-form-field appearance="fill">
                <mat-label>Select</mat-label>
                <mat-select>
                    <mat-option value="option">Option</mat-option>
                </mat-select>
            </mat-form-field>
            <br>
            <mat-form-field appearance="fill">
                <mat-label>Textarea</mat-label>
                <textarea matInput></textarea>
            </mat-form-field>
        </div>

    </mat-grid-tile>
    <mat-grid-tile>
        <mat-slide-toggle>Slide me!</mat-slide-toggle>
    </mat-grid-tile>
</mat-grid-list>

app.component.ts (import文, constructor(), openBottomSheet()を追加)

import { Component } from '@angular/core';
//追加 start
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { BottomSheetOverviewExampleSheet } from './bottom-sheet-overview.component';
//追加 end

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

  title = 'my-app';

  //追加 start
  constructor(private _bottomSheet: MatBottomSheet) {} 
  openBottomSheet(): void {
    this._bottomSheet.open(BottomSheetOverviewExampleSheet);
  }
  //追加 end
}

app.module.ts (import文, @NgModuleのdeclarations, importsを追加)

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

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

//追加 start
import { MatButtonModule } from '@angular/material/button';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { BottomSheetOverviewExampleSheet } from './bottom-sheet-overview.component';
import { MatListModule } from '@angular/material/list';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
//追加 end

@NgModule({
  declarations: [
    AppComponent, //カンマ追加
    //追加 start
    BottomSheetOverviewExampleSheet,
    //追加 end
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule, //カンマ追加

    //追加 start
    MatButtonModule,
    MatBottomSheetModule,
    MatListModule,
    MatGridListModule,
    MatInputModule,
    MatSelectModule,
    MatSlideToggleModule,
    //追加 end
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
新規ファイルの追加

bottom-sheet-overview-example-sheet.html

<mat-nav-list>
    <a href="https://keep.google.com/" mat-list-item (click)="openLink($event)">
        <span mat-line>Google Keep</span>
        <span mat-line>Add to a note</span>
    </a>

    <a href="https://docs.google.com/" mat-list-item (click)="openLink($event)">
        <span mat-line>Google Docs</span>
        <span mat-line>Embed in a document</span>
    </a>

    <a href="https://plus.google.com/" mat-list-item (click)="openLink($event)">
        <span mat-line>Google Plus</span>
        <span mat-line>Share with your friends</span>
    </a>

    <a href="https://hangouts.google.com/" mat-list-item (click)="openLink($event)">
        <span mat-line>Google Hangouts</span>
        <span mat-line>Show to your coworkers</span>
    </a>
</mat-nav-list>

bottom-sheet-overview.component.ts

import {Component} from '@angular/core';
import {MatBottomSheet, MatBottomSheetRef} from '@angular/material/bottom-sheet';

@Component({
  selector: 'bottom-sheet-overview-example-sheet',
  templateUrl: 'bottom-sheet-overview-example-sheet.html',
})
export class BottomSheetOverviewExampleSheet {
  constructor(private _bottomSheetRef: MatBottomSheetRef<BottomSheetOverviewExampleSheet>) {}

  openLink(event: MouseEvent): void {
    this._bottomSheetRef.dismiss();
    event.preventDefault();
  }
}
アプリケーションを起動
# pwd   
/home/tworks55/dev/angular/my-app
# npm start

> my-app@0.0.0 start /home/tworks55/dev/angular/my-app
> ng serve


chunk {main} main.js, main.js.map (main) 29.6 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 141 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.15 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 156 kB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 4.6 MB [initial] [rendered]
Date: 2020-05-10T12:50:18.401Z - Hash: d330c857cd5e622231f1 - Time: 12429ms
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
: Compiled successfully.

Compiled successfullyと表示されていたら、http://localhost:4200/にアクセスし画面を操作することができます。

f:id:tworks55:20200510215910g:plain

ディレクトリ構成

ディレクトリ構成はこのようになっています。

f:id:tworks55:20200510215421p:plain

今回使ったAngularとAngular Materialのバージョン

angularは9.1.6でangular/materialは9.2.3となっています。

{
  "name": "my-app",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~9.1.6",
    "@angular/cdk": "^9.2.3",
    "@angular/common": "~9.1.6",
    "@angular/compiler": "~9.1.6",
    "@angular/core": "~9.1.6",
    "@angular/forms": "~9.1.6",
    "@angular/material": "^9.2.3",
    "@angular/platform-browser": "~9.1.6",
    "@angular/platform-browser-dynamic": "~9.1.6",
    "@angular/router": "~9.1.6",
    "rxjs": "~6.5.4",
    "tslib": "^1.10.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.901.5",
    "@angular/cli": "~9.1.5",
    "@angular/compiler-cli": "~9.1.6",
    "@types/node": "^12.11.1",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "^5.1.2",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~5.0.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~3.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "protractor": "~5.4.3",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~3.8.3"
  }
}

Reactの場合はMATERIAL-UIがありますが、コンポーネントの種類がAngular Materialより充実しているように見えます。

ただし、フレームワークとしてはAngularの方が使いやすいような気がしているのでFront-end開発の上級者でない場合は選択が悩ましいところです。

いずれにせよリリースサイクルが早くBreaking Changeが入るのは変わらないので、ある程度のスキルがある人がいないとメンテナンスは難しいですね。