[Angular]入力フォームを追加削除できる動的なフォームを作ってみる

2020.01.22

どうも!大阪オフィスの西村祐二です。

今回は下記のようなボタンをクリックするとフォームを追加したり、削除できるような仕組みを作ってみたいと思います。

作ってみる

環境

Angular CLI: 8.3.23
Node: 12.13.0
OS: darwin x64
Angular: 8.2.14
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.803.23
@angular-devkit/build-angular     0.803.23
@angular-devkit/build-optimizer   0.803.23
@angular-devkit/build-webpack     0.803.23
@angular-devkit/core              8.3.23
@angular-devkit/schematics        8.3.23
@angular/cli                      8.3.23
@ngtools/webpack                  8.3.23
@schematics/angular               8.3.23
@schematics/update                0.803.23
rxjs                              6.4.0
typescript                        3.5.3
webpack                           4.39.2

セットアップ

$ ng new dynamic-form --style=scss --routing
$ cd dynamic-form

実装

いろいろやり方はありますが、FormBuilderを使って実装していきます。

詳しくはこちらを参照ください。

まずは、ReactiveFormsModuleを使うためにモジュールをimportしておきます。

src/app/app.module.ts

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

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

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, ReactiveFormsModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

▼ロジック部分

簡単な解説コメントをいれています。

ポイントは

  • 追加したいフォームをreturnでFormGroupを返すようにしています。

  • 追加のボタンをクリックされたときpushすることでFormArrayにフォームを追加することができます。

  • 削除したいときはFormArrayに対してインデックス指定したremoveAtすることで削除することができます。

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, FormArray } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.initForm();
  }
  // FormBuilderを使って初期フォームを作成します。フォームの塊のFormGroupとしています。
  initForm() {
    this.form = this.fb.group({
      name: [''],
      price: [100],
      // フォームを追加したり、削除したりするために、FormArrayを設定しています。
      options: this.fb.array([])
    });
  }
  
  // 追加ボタンがおされたときに追加したいフォームを定義しています。returnでFormGroupを返しています。
  get optionForm(): FormGroup {
    return this.fb.group({
      name: [''],
      value: ['']
    });
  }

  // FormのOption部分を取り出しています。
  // テンプレートでオプション部分を表示するときやフォームの追加・削除のときに利用します。
  // 型をFormArrayとすることがポイントです
  get options(): FormArray {
    return this.form.get('options') as FormArray;
  }

  // 追加ボタンがクリックされたときに実行する関数です。
  // pushとすることで既存のフォームにオプション用のフォームを追加します。
  addOptionForm() {
    this.options.push(this.optionForm);
  }
  
  // removeAtでインデックスを指定することで、FormArrayのフォームを削除します。
  removeOptionForm(idx: number) {
    this.options.removeAt(idx);
  }
}

▼テンプレート部分の実装

簡単な解説コメントをいれています。

ポイントは

  • 9行目ではoptions関数を実行しArrayFormを指定してます。

  • FormArray内のFormGroupの名前はindexになります。

  • Debugするときはform.valueとjson pipeを使って表示しておくと設定がうまくいってる確認しやすいです。

src/app.component.html

<h3>Form Test</h3>

<form [formGroup]="form">
  <div>商品名:<input type="text" formControlName="name" /></div>
  <div>価格:<input type="number" formControlName="price" /></div>

  <p>オプション:</p>
  <!-- フォームのオプションを指定(ArrayForm部分) -->
  <div formArrayName="options">
    <!-- for文を回して、表示 -->
    <div *ngFor="let option of options.controls; let i = index">
      <!-- 中にあるFromGroupの名前はindex名になるのがポイント -->
      <div [formGroupName]="i">
        <div>名前:<input type="text" formControlName="name" /></div>
        <div>値:<input type="text" formControlName="value" /></div>
      </div>
      <!-- 削除するときはindexが必要になるので引数で渡す -->
      <button (click)="removeOptionForm(i)">削除</button>
    </div>
  </div>
  <button (click)="addOptionForm()">追加</button>
</form>

<!-- Debug用の表示 -->
<h3>Debug</h3>
<ng-container *ngIf="form.value">
  <pre>{{ form.value | json }}</pre>
</ng-container>

さいごに

入力フォームを追加削除できる動的なフォームを作ってみました。

フォームは管理画面やいろんな場面で利用すると思いますので、使いこなせると幅が広がります。

また、今回紹介した以外にいろいろ機能がありますので別途紹介していきたいと思います。

興味のある方は是非試してみてください。

誰かの参考になれば幸いです。