Reputation: 526
I'd like to listen to valuechanges of my form, but not for the entire form but only for the formcontrol that was changed.
If for example my form looks like this.
this.form = this._fb.group({
firstName: [''],
lastName: [''],
... // other stuff.
});
If I then subscribe to valuechanges
this.form.valueChanges.subscribe((e) => {
console.log(e);
});
Then filling in a firstname in the form would result in a printout of the entire form value object.
{firstName: 'input', lastName: '', ...}
But what I want to know is which form control (in this case the firstName
) was altered without subscribing to each individual form control. Such that my desired output is only
{firstName: 'input'}
Upvotes: 6
Views: 10155
Reputation: 11934
Firstly, multiple AbstractControl
s(e.g FormControl,
FormGroup
, FormArray
) are stored internally as a tree.
// FG - FormGroup
// FA - FormArray
// FC - FormControl
FG
/ \
FC FG
/ \
FC FA
/ | \
FC FC FC
The above snippet is excerpted from A thorough exploration of Angular Forms.
Because of that reason, a FormGroup.valueChanges
emits when a FormGroup
child's valueChanges
emits:
updateValueAndValidity (/* ... */) {
/* ... */
if (opts.emitEvent !== false) {
(this.valueChanges as EventEmitter<any>).emit(this.value);
(this.statusChanges as EventEmitter<string>).emit(this.status);
}
if (this._parent && !opts.onlySelf) {
this._parent.updateValueAndValidity(opts);
}
/* ... */
}
In the above snippet, if we were to consider your example, _parent
could refer to the this.form
FormGroup
instance when the firstName
FormControl
receives some input.
So, a way to achieve what you're looking for would be this:
merge(
...Object.keys(this.form.controls).map(
k => this.form.controls[k].valueChanges.pipe(
// Getting a decent format: `{ formControlName: formControlValue }`
map(v => ({ [k]: v })),
)
)
).subscribe(console.log)
The FormGroup.controls
has this signature:
public controls: {[key: string]: AbstractControl},
Upvotes: 5
Reputation: 71
There are multiple ways to listen form control changes:
custom validation method:
this.form = this._fb.group({
firstName: ['', [this.fieldValidator]],
lastName: [''],
... // other stuff.
});
fieldValidator(control: FormControl) {
// control?.value available here
// return null
}
or cross-validation method:
this.form = this._fb.group({
firstName: [''],
lastName: [''],
... // other stuff.
}, { validators: exampleValidator});
export const exampleValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const firstName = control.get('firstName');
// firstName?.value available here, etc
// return condition ? null : { inValid: true};
};
Upvotes: 0
Reputation: 101
You can try something like this
this.form.get("firstName").valueChanges.subscribe(selectedValue => {
console.log(selectedValue)
})
Upvotes: 3