1
0

More than 3 years have passed since last update.

Angular(v8), ReactiveForm, boolean以外を使うときのチェックボックスフォームの構築の仕方

Last updated at Posted at 2019-10-06

はじめに

ReactiveFormのチェックボックスでboolean以外のvalueを扱うときのフォームの構築の仕方をまとめました。
ReactiveForm自体については説明しないので詳しいところは公式ドキュメントをご覧になってください

こんな課題があったとする

  • ReactiveFormでチェックボックスを使いたいけど値はbooleanを扱いたくない。

  • フォームの見た目は以下の画像のようだけど、使用者によって入力されたフォームの値を扱うときはイヌ → Dog, ネコ → Cat, ネズミ → Ratのように各チェックボックスに紐づいた別の値を使用したい。
    スクリーンショット 2019-10-05 16.28.05.png

この場合どうするか。

解決策は2つ

フォームモデルをテンプレートファイルにバインドせずにchangeイベントを利用してフォームモデルを更新させる。

フォームモデルをテンプレートファイルにバインドさせて、フォームの値を取得するときにbooleanから欲しい値に変換させる。

実際のコードを下に載せたいと思います。

①のコード 

(form1.component.ts)
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';

interface Option {
  label: string;
  value: string;
}

@Component({
  selector: 'app-form1',
  templateUrl: './form1.component.html'
})
export class Form1Component {
  readonly animalsOption: Option[] = [
    { label: 'イヌ', value: 'Dog' },
    { label: 'ネコ', value: 'Cat' },
    { label: 'ネズミ', value: 'Rabbit' }
  ];
  ngForm: FormGroup = this.fb.group({
    animals: this.fb.array([])
  });
  showValue: string[] = [''];

  get animals(): FormArray {
    return this.ngForm.get('animals') as FormArray;
  }

  constructor(private fb: FormBuilder) {}

  onSubmit() {
    console.log(this.ngForm);
    this.showValue = this.animals.value;
  }

  onChange(value: string) {
    const array = this.animals.getRawValue();
    array.includes(value)
      ? this.animals.removeAt(array.indexOf(value))
      : this.animals.push(this.fb.control(value));
  }
}
(form1.html)
<form [formGroup]="ngForm" (ngSubmit)="onSubmit()">
  <div>
    <label *ngFor="let option of animalsOption">
      <input type="checkbox" (change)="onChange(option.value)" />
      {{ option.label }}
    </label>
  </div>
  <button class="button" type="submit">
    Show Value!
  </button>
  <h4>form1 value -> {{ showValue }}</h4>
</form>

フォームモデルをテンプレートファイルにバインドさせていないので、
FormArrayremoveAt()メソッドとpush()メソッドを使用し、チェックボックスの状態に応じてFormControlを追加、削除を動的に行なっています。

②のコード 

(form2.component)
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';

interface Option {
  label: string;
  value: string;
}

@Component({
  selector: 'app-form2',
  templateUrl: './form2.component.html'
})
export class Form2Component {
  readonly animalsOption: Option[] = [
    { label: 'イヌ', value: 'Dog' },
    { label: 'ネコ', value: 'Cat' },
    { label: 'ネズミ', value: 'Rabbit' }
  ];
  ngForm: FormGroup = this.fb.group({
    animals: this.fb.array(this.animalsOption.map(() => this.fb.control(false)))
  });
  showValue: string[] = [''];

  get animals(): FormArray {
    return this.ngForm.get('animals') as FormArray;
  }

  constructor(private fb: FormBuilder) {}

  onSubmit() {
    console.log(this.ngForm);
    this.showValue = this.convertFormValue(this.animals, this.animalsOption);
  }

  private convertFormValue(formArray: FormArray, option: Option[]): string[] {
    const values: boolean[] = formArray.value;
    return values
      .map((val, index) => {
        return val ? option[index].value : undefined;
      })
      .filter(v => v);
  }
}

(form2.html)
<form [formGroup]="ngForm" (ngSubmit)="onSubmit()">
  <div>
    <span formArrayName="animals">
      <label *ngFor="let _ of ngForm.controls.animals.controls; let i = index">
        <input type="checkbox" [formControlName]="i" />
        {{ animalsOption[i].label }}
      </label>
    </span>
  </div>
  <button class="button" type="submit">
    Show Value!
  </button>
  <h4>form2 value -> {{ showValue }}</h4>
</form>

FormArrayの値(boolean[])と定義した変換させたい配列(animalsOption)を参照させて欲しい値に変換させています。

それぞれの所感

  • はテンプレートファイルとフォームモデルが疎結合な状態であるので、チェック操作以外でフォームの値を変更する場合(例えばフォームのリセット操作(reset())につらい。 FormArrayで定義されているメソッドを使っているので可読性が高いと感じた。

  • はテンプレートファイルとフォームモデルが密結合な状態であるので、のようなつらさが出ることはない。ただ、FormArrayの値を変換するところが若干、書いてて気持ちが悪い。 (改善案求めます)

おわりに

コードベタ貼りだとわかりづらいと思うので
stackblitz に上記のフォームを実装したので詳しく動作確認してください。

1
0
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
1
0