オフィス狛 技術部のmmm(むー)です。
先日Angularにて、Reactive Formを使用し、要素が動的に増えるフォームを作成したかったのですが、公式のページを読んでもなかなかイメージがわかず、苦労したので備忘録として残します。
今回、記事のためにAngularのバージョンは13.2.0を使用して確認しました。
業務で使用した際、Angular8系で動くことも確認しています。
対応方法
作成したかったフォームの構造は下記のようなイメージです。「ID、名前、メモ」で1セットのデータが動的に増減します。 早速ですが、コンポーネントのコードを記載します。 // app.component.ts
import { Component, VERSION } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
constructor(private formBuilder: FormBuilder) {}
// フォーム(枠) = formタグに該当します
formGroup: FormGroup = this.formBuilder.group({
features: this.formBuilder.array([]),
});
// 実際はAPIからの戻り値を使用すると思いますが、今回は確認用に以下の値を定義します
list = [
{ id: 1, name: 'Haru', memo: '春' },
{ id: 2, name: 'Natsu', memo: '夏' },
];
ngOnInit() {
// 空のフォーム(中身) = formタグの中に入れる項目をまとめたものになります
// 今回の例ですと、「ID、名前、メモ」で1つの単位となります。
let testForm = this.formBuilder.group({});
// 初期値とバリデーションを設定します。
this.list.forEach((obj) => {
testForm = this.formBuilder.group({
name: [obj.name, Validators.required],
memo: [obj.memo],
});
// フォーム(中身)をフォーム(枠)に追加します
this.features.push(testForm);
});
}
// featuresはAbstractControlを返すので、型をFormArrayにします
get features(): FormArray {
return this.formGroup.get('features') as FormArray;
}
}
・FormArrayを使用し、各FormControlの値をまとめることができます。
・FormControlは1つの入力項目(例えば、inputタグ単位)となります。
・更に、FormGroupを使用し、各FormArrayの値をまとめることができます。
・FormArrayが不要な場合は、FormGroupとFormControlだけで問題ありません。
HTMLは下記となります。(バリデーション等は記載していません)
// app.component.html
<form [formGroup]="formGroup">
<ng-container formArrayName="nestForm">
<!-- グループ名(formGroupName)はインデックスになります -->
<ng-container *ngFor="let val of nestForm.controls; let i = index">
<ng-container [formGroupName]="i">
<p>ID:{{ list[i].id }}</p>
<p>
名前:<input
type="text"
class="form-control"
formControlName="name"
/>
</p>
<p>
メモ:<input
type="text"
class="form-control"
formControlName="memo"
/>
</p>
<hr />
</ng-container>
</ng-container>
</ng-container>
<button type="submit" [disabled]="formGroup.invalid ">更新</button>
</form>
このコードにバリデーションをチェックし、エラーメッセージを表示する処理を追加したい場合、以下のように作成することができます。
FormControlの名前を固定できるので(下記の例では、「name」)、バリデーションチェック用の処理を重複して書く必要がありません。
// app.component.ts
// 名前が入力されているかチェックする
errorNameRequired(i: number): boolean {
return (this.formArray.get('features')).controls[i].get('name').hasError('required');
}
// app.component.html
<p>
名前:<input
type="text"
class="form-control"
formControlName="name"
/>
<!-- エラーメッセージを表示するためのコードを追加↓ -->
<ng-container *ngIf="errorNameRequired(i)">名前を入力してください。</ng-container>
</p>
ちなみにフォームの値に直接アクセスしたい場合は、以下のように確認することができます。
console.log(this.nestForm.value[0].name) // Haru
以上となります。参考になりましたら幸いです。
参考:
https://angular.io/api/forms/FormGroup (公式)
https://angular.io/api/forms/FormArray (公式)
Angular
0 件のコメント:
コメントを投稿