13
8

More than 5 years have passed since last update.

AngularのReactiveFormとAngularMaterialを組み合わせる

Posted at

はじめに

Vue人気も一段落ついた感があるので、ここからAngularを盛り上げる(?)ためにも何か投稿せねば!!(その2)

Angularの強みといえるフォーム周りですが、Validationを入れるならやっぱりReactiveFormFormGroupFormBuilderが分かりやすいし、楽だと思います。

それをMaterialDesign用コンポーネントを提供してくれるAngular Materialのフォーム系に使いたい。だけど公式ドキュメントのExampleには単純なFormControlを利用したものしかないです。

というわけでやってみました。

準備

まずはng newするなりして、Angularが書ける環境を用意して、主役の「Angular Material」を追加します。

$: npm install --save @angular/material @angular/cdk @angular/animations

※Yarn派はyarnコマンドに置き換えてください。

app.module.tsBrowserAnimationsModuleと使用する予定の各Materialコンポーネント(Mat○○Module)を追加します。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import {
  MatButtonModule,
  MatFormFieldModule,
  MatInputModule,
  MatSelectModule,
} from '@angular/material';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    CommonModule,
    BrowserModule,
    BrowserAnimationsModule,
    ReactiveFormsModule,
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule,
    MatSelectModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

加えてcssも適用します。

styles.css
@import "~@angular/material/prebuilt-themes/indigo-pink.css";

テーマとか各コンポーネントの詳しいことは公式ドキュメントを確認ください。

コンポーネント

面倒くさいので今回はAppComponentに全部書いちゃいます。

フォームの項目およびValidationタイプは

  • お名前(テキスト)  必須
  • 性別(セレクト)   必須+「未選択(0)」以上
  • Eメール(テキスト) メールタイプ

と何の芸もない組み合わせにしています。

ReactiveFormなので各入力項目のフォーカスを抜けるとValidationが走り、エラーがあれば表示されます。

app.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';

// 性別用型
interface Gender {
  value: number;
  display: string;
}

@Component({
  selector: 'app-root',
  template:
  `
  <form [formGroup]="editForm" (ngSubmit)="onSave()" class="form">
    <mat-form-field class="form-field">
      <input matInput placeholder="お名前" formControlName="name" required>
      <mat-error *ngIf="name.invalid && (name.dirty || name.touched)">お名前は必須です</mat-error>
    </mat-form-field>
    <mat-form-field class="form-field">
      <mat-select placeholder="性別" formControlName="genderValue" required>
        <mat-option *ngFor="let gender of genders" [value]="gender.value">
          {{ gender.display }}
        </mat-option>
      </mat-select>
      <mat-error *ngIf="genderValue.invalid && (genderValue.dirty || genderValue.touched)">性別は必須です</mat-error>
    </mat-form-field>
    <mat-form-field class="form-field">
      <input matInput placeholder="Eメール" formControlName="mail">
      <mat-error *ngIf="mail.invalid && (mail.dirty || mail.touched)">Eメールが不正です</mat-error>
    </mat-form-field>
    <div class="action">
      <button type="submit" mat-button color="primary" [disabled]="editForm.invalid">SAVE</button>
    </div>
  </form>
  `,
  styles: [
    `
      :host {
        display: box;
        box-sizing: border-box;
      }
      .form {
        margin: 8px;
        padding: 8px;
      }
      .form-field {
        width: 100%;
      }
      .action {
        display: flex;
        justify-content: space-around;
      }
    `,
  ]
})
export class AppComponent implements OnInit {
  // 性別一覧
  genders: Gender[] = [
    {
      value: 0,
      display: '未選択',
    },
    {
      value: 1,
      display: '',
    },
    {
      value: 2,
      display: '女性',
    },
    {
      value: 3,
      display: 'どちらでもない',
    },
  ];

  // FormGroup
  editForm: FormGroup;


  // 各フォームコントロールを取得するGetter
  get name(): AbstractControl {
    return this.editForm.get('name');
  }
  get genderValue(): AbstractControl {
    return this.editForm.get('genderValue');
  }
  get mail(): AbstractControl {
    return this.editForm.get('mail');
  }

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.editForm = this.fb.group({
      name: ['', [Validators.required]],
      genderValue: [0, [Validators.required, Validators.min(1)]],
      mail: ['', [Validators.email]],
    });
  }

  onSave() {
    console.log(this.editForm.value);
  }
}

RUN

$: ng serve

初期表示
image.png

All Invalid
image.png

All Valid
image.png

この状態で「SAVE」をクリックするとブラウザの開発用コンソールに{name: "てすとくん", genderValue: 1, mail: "testkun@test.com"}と表示されるはずです。

おわりに

いつも思うけど日本語表記にするとMaterial感が目減りする気がするのは私だけでしょうか?

Angularはフォーム周りは本当に強いと思ってるのでもっとAngularが流行ればいいなぁ。

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8