AngularでFormArrayのpushとremoveAtメソッドを使ってフォーム入力履歴を保持する方法
Angular、Angular2 Forms、Angular2 FormBuilderにおけるForm ControlのvalueChangesイベントと前値取得
概要
解説
valueChanges
イベントは、フォームコントロールの値が変更されたタイミングで発生するイベントです。このイベントは、フォームコントロールの値が直接変更された場合だけでなく、プログラム的に値を設定した場合も発生します。
前値の取得方法
valueChanges
イベントの引数として、配列が渡されます。この配列の先頭要素には、現在の値が格納されており、2番目の要素には変更前の値が格納されています。
具体的なコード例
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
name: new FormControl(''),
email: new FormControl('')
});
}
ngOnInit() {
this.myForm.controls['name'].valueChanges.subscribe((values) => {
const currentValue = values[0]; // 現在の値
const previousValue = values[1]; // 変更前の値
console.log(`Name: ${currentValue} (from ${previousValue})`);
});
}
}
このコード例では、name
フォームコントロールの valueChanges
イベントに購読し、現在の値と変更前の値をコンソールに出力しています。
利用例
- フォーム入力履歴の保持
- フォーム入力の条件分岐
- フォーム入力のバリデーション
注意事項
valueChanges
イベントは、フォームコントロールの値が変更されたタイミングで非同期に発生します。そのため、イベントハンドラー内で直接 DOM 操作を行う場合は注意が必要です。- 前値を取得するには、
valueChanges
イベントの引数として渡される配列の2番目の要素にアクセスする必要があります。
HTML (app.component.html)
<form [formGroup]="myForm">
<input type="text" formControlName="name" placeholder="名前">
<input type="email" formControlName="email" placeholder="メールアドレス">
<button type="button" (click)="submit()">送信</button>
</form>
TypeScript (app.component.ts)
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
name: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email])
});
}
ngOnInit() {
this.myForm.controls['name'].valueChanges.subscribe((values) => {
const currentValue = values[0];
const previousValue = values[1];
console.log(`Name: ${currentValue} (from ${previousValue})`);
});
}
submit() {
console.log(this.myForm.value);
}
}
form {
display: flex;
flex-direction: column;
margin: 20px;
}
input {
margin-bottom: 10px;
}
button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
このコード例では、以下の機能が実装されています。
name
とemail
という2つのフォームコントロールを持つフォームを作成します。name
フォームコントロールには必須バリデーションを追加します。email
フォームコントロールには必須バリデーションとメールアドレス形式バリデーションを追加します。- 送信ボタンをクリックすると、フォームの値をコンソールに出力します。
このサンプルコードを参考に、ご自身のアプリケーションに実装してみてください。
Angular、Angular2 Forms、Angular2 FormBuilderにおけるForm ControlのvalueChangesイベントと前値取得の代替方法
FormArray と push() メソッド
FormArray
を使用して、フォームコントロールの値を配列として管理することができます。新しい値を追加するたびに、push()
メソッドを使用して配列に値を追加することで、履歴を保持することができます。
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
nameValues: FormArray = new FormArray([]);
constructor() { }
ngOnInit() {
this.myForm.controls['name'].valueChanges.subscribe((value) => {
this.nameValues.push(value);
});
}
}
このコード例では、nameValues
という FormArray
を作成し、name
フォームコントロールの値変更を検知するたびに、push()
メソッドを使用して配列に値を追加しています。
カスタムオペレーターを作成して、Form Controlの値変更を検知し、前値を取得することができます。
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Operator } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
name: new FormControl('')
});
}
ngOnInit() {
const previousValueOperator: Operator<string, string> = (source$) => source$.pipe(
scan((acc, value) => [value, acc], [])
);
this.myForm.controls['name'].valueChanges.pipe(
previousValueOperator
).subscribe(([currentValue, previousValue]) => {
console.log(`Name: ${currentValue} (from ${previousValue})`);
});
}
}
このコード例では、previousValueOperator
というカスタムオペレーターを作成し、scan()
オペレーターを使用して、現在の値と変更前の値を保持しています。
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, fromEvent } from '@angular/forms';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
name: new FormControl('')
});
}
ngOnInit() {
const nameControl$: Observable<string> = fromEvent(this.myForm.controls['name'], 'valueChange');
nameControl$.pipe(
scan((acc, value) => [value, acc], [])
).subscribe(([currentValue, previousValue]) => {
console.log(`Name: ${currentValue} (from ${previousValue})`);
});
}
}
このコード例では、fromEvent()
関数を使用して name
フォームコントロールの値変更イベントを Observable に変換し、scan()
オペレーターを使用して、現在の値と変更前の値を保持しています。
その他の方法
FormGroup
のvalueChanges
イベントを使用するFormArray
のpush()
メソッドとremoveAt()
メソッドを使用する- カスタムディレクティブを作成する
結論
valueChanges
イベント以外にも、Form Controlの値変更を検知し、前値を取得する方法があります。それぞれの方法にはメリットとデメリットがあるので、状況に合わせて最適な方法を選択してください。
- [Angular 公式ドキュメント - FormControl](https
angular angular2-forms angular2-formbuilder