I have form group in my angular app. Since I would like to aviod writing onChange for each form element, my idea was to subscribe to entire form changes, and then on event check changed element
So this is relevant part of the code:
constructor(){
this.orderForm = this.formBuilder.group({
...
});
this.event$ = this.orderForm.valueChanges.subscribe((x:any) => {
console.log('form value changed')
console.log(x)
});
Problem is, this x is just entire form so I have no idea what element is changed
Is there any smart way to find out what form element changed?
CodePudding user response:
This works well if you have a simple form with just formcontrols, no nested groups or FormArrays.
You can use rxjs merge
for each form controls value changes and figure out which one changed:
merge(
...Object.keys(this.orderForm.controls).map(
(controlName: string) =>
this.orderForm.get(controlName).valueChanges.pipe(
tap((value) => {
console.log(`Control "${controlName}" changed. New value: ${value}`)
})
)
)
).subscribe();
Here's a STACKBLITZ for your reference.
CodePudding user response:
I don't thing there is a function that returns only the formControls that have changed.
But since valueChanges is an observeable you could use pairwise
to get the previous and the next value. And compare them to find out what has changed.
this.orderForm.valueChanges
.pipe(startWith(null), pairwise())
.subscribe(([prev, next]: [any, any]) => ... );
CodePudding user response:
"stolen" the idea of AT82, we can has a recursive function that return an observable. Well, in stead of use "tap", we can use map to return an object with two properties: name (the control has changed) and value.
So some like
mergeForm(form: FormGroup | FormArray, sufix: string) {
if (form instanceof FormArray) {
console.log(sufix)
const formArray = form as FormArray;
const arr = [];
for (let i = 0; i < formArray.controls.length; i ) {
const control = formArray.at(i);
arr.push(
control instanceof FormGroup
? this.mergeForm(control, sufix i '.')
: control.valueChanges.pipe(
map((value) => ({ name: sufix i, value: value }))
)
);
}
return merge(...arr);
}
return merge(
...Object.keys(form.controls).map((controlName: string) => {
const control = form.get(controlName);
return control instanceof FormGroup || control instanceof FormArray
? this.mergeForm(control, sufix controlName '.')
: control.valueChanges.pipe(
map((value) => ({ name: sufix controlName, value: value }))
);
})
);
}
We can call in the way
this.mergeForm(this.orderForm, '')
.pipe(takeWhile((_) => this.alive))
.subscribe((res) => {
console.log(res);
});
You can see the stackblitz
NOTE: If you don't use FormArray you can simply the recursive function