中安拓也のブログ

プログラミングについて書くブログ

Angular Materialでログインフォームを作る

f:id:l08084:20180420160959g:plain

Angular Materialを使って、ログイン用のフォームを作ります

Angular Materialのインストールから解説している記事はこちら

バージョン情報

  • Angular: 5.2.9

  • Node: 8.1.4

  • @angular/material: 5.2.4

テンプレート駆動型とモデル駆動型

Angularのフォームには、テンプレート駆動型とモデル駆動型があります

  • テンプレート駆動型のフォーム

    • 検証ルールをテンプレート側に記述する
    • モデル駆動型よりも手軽に実装できる
    • 複雑な要件には向いていない
    • NgModuleFormsModuleのインポートが必要
  • モデル駆動型のフォーム

    • 検証ルールをコンポーネント側に記述する
    • コードが冗長になりやすい
    • 複雑な要件でも表現できる
    • NgModuleReactiveFormsModuleのインポートが必要

今回は、簡単に書けそうなテンプレート駆動型のフォームを採用します。ただ、Angular Materialで作られたフォームのサンプルを見ると、モデル駆動型のフォームで記述されているものが多い気がするので、後々問題が発生したら、モデル駆動型に切り替えます。

NgModuleの設定

ログインフォーム作成のために、下記のAngular Materialコンポーネントを使ったので、NgModuleに設定していきます。

  • MatButtonModule

  • MatCardModule

  • MatInputModule

  • MatFormFieldModule

  • MatIconModule

また、今回はAngularのテンプレート駆動型のフォームを使うので、FormsModuleの設定も必要です。

src/app/app.module.ts:

import { FormsModule } from '@angular/forms';

import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';


@NgModule({
  declarations: [AppComponent, ExchangeListComponent, PrivateComponent],
  imports: [
    // add this!
    MatButtonModule,
    MatCardModule,
    MatInputModule,
    MatFormFieldModule,
    MatIconModule,
    FormsModule,
    // ...省略
  ],
// ...省略

下記の手順を実行してください

  1. 上記のモジュール(MatFormFieldModule他6つ)をインポート

  2. @NgModuleデコレータで定義されているimports:[]リストに上記の6つのモジュールを追加する

index.htmlにアイコンのCDNを追加

マテリアルアイコンをパスワード欄で使うので、下記のCDNをindex.htmlに追加する必要があります。

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

src/app/index.html:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>CriptocurrencyApiCall</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>

<body>
  <app-root></app-root>
</body>

</html>

ログインフォームを作る

f:id:l08084:20180420161351p:plain

下記の要件に従って、ログインフォームを作ります。

  • ログインフォームの要件
    • API Key欄とAPI Secret欄がある
    • API Secret欄は、目のアイコンをクリックすると、type=passwordが解除される
    • バリデーションは、API Key欄とAPI Secret欄の必須チェックのみ
    • バリデーションのチェックが全部OKにならないと、Loginボタンはdisable
    • 必須チェックを満たしていない欄には、<mat-error>によるエラーメッセージが表示される

src/app/private/private.component.html:

<mat-card class="login-card">
  <mat-card-header>
    <mat-card-title class="login-title">bitFlyer API</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <form (ngSubmit)="onSubmit()" class="login-form" #loginForm="ngForm">
      <mat-form-field>
        <input matInput placeholder="API Key" id="key" [(ngModel)]="keySet.key" name="key" #key="ngModel" required>
        <mat-error *ngIf="key.errors?.required">You must enter a value</mat-error>
      </mat-form-field>

      <mat-form-field>
        <input [type]="hide ? 'password' : 'text'" matInput placeholder="API Secret" id="secret" [(ngModel)]="keySet.secret" name="secret"
          #secret="ngModel" required>
        <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 'visibility_off'}}</mat-icon>
        <mat-error *ngIf="secret.errors?.required">You must enter a value</mat-error>
      </mat-form-field>
      <button type="submit" mat-raised-button color="primary" [disabled]="loginForm.invalid">Login</button>
    </form>
  </mat-card-content>
</mat-card>

src/app/private/private.component.ts:

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

export class PrivateAPIKeySet {
  constructor(public key: string, public secret: string) {}
}

@Component({
  selector: 'app-private',
  templateUrl: './private.component.html',
  styleUrls: ['./private.component.scss']
})
export class PrivateComponent implements OnInit {
  keySet = new PrivateAPIKeySet('', '');
  hide = true;
  constructor() {}

  ngOnInit() {}

  onSubmit() {
    console.log(this.keySet);
  }
}

src/app/private/private.component.scss:

.login-card {
  max-width: 400px;
}

.login-form {
  display: flex;
  flex-direction: column;
}

.login-title {
  font-weight: bold;
  font-size: 22px;
  color: #3f51b5;
}

参考サイト

https://material.angular.io/components/form-field/overview