Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify which item in FormArray emitted valueChanges event?

Tags:

In Angular, is there a way to identify which FormGroup/FormControl in a dynamicFormArray emitted the valueChanges event?

My FormArray is dynamic. It starts out empty and users could add a FormGroup to the FormArray by clicking a button.

When valueChanges, I need to re-validate the control. Since I dont know which control emitted the event, I loop through the entire FormArray and validate all FormGroup/FormControl even though only one control changed - and this is every time when anything in the array changes. How can I avoid doing this?

        this.myFormArray         .valueChanges         .subscribe(data => this.onValueChanged(data));      onValueChanged(data?: any): void {      // the data I receive is an entire form array.     // how can I tell which particular item emitted the event,      // so I don’t need to loop through entire array and run validation for all items.      for (let control in this.myFormArray.controls) {         // run validation on each control.     } } 
like image 304
anish Avatar asked Dec 06 '18 15:12

anish


People also ask

How do I use ValueChanges in FormArray?

The ValueChanges is an event raised by the Angular forms whenever the value of the FormControl, FormGroup or FormArray changes. It returns an observable so that you can subscribe to it. The observable gets the latest value of the control. It allows us to track changes made to the value in real-time and respond to it.

How do I remove all elements from FormArray?

You can manually clear each FormArray element by calling the removeAt(i) function in a loop. The advantage to this approach is that any subscriptions on your formArray , such as that registered with formArray. valueChanges , will not be lost.

How does angular define FormArray?

First, we need to import the FormArray from the Angular Forms Module. Build a formGroup orderForm using the FormBuilder. We define items as FormArray. We need to capture two fields under each item, the name of the item & description and price.


1 Answers

To build on epsilon's answer, which collects an array of valueChanges observables and merges them - you can also pipe the value changes thru a pipe, which adds necessary context to the changes stream via map.

merge(...this.formArray.controls.map((control: AbstractControl, index: number) =>         control.valueChanges.pipe(map(value => ({ rowIndex: index, value })))))       .subscribe(changes => {         console.log(changes);       }); 

Output:

{    rowIndex: 0   value: {     <current value of the changed object>   } } 

Note that the first call to map (on controls) is on an array. The second map (in the pipe) is an RxJs map. I really like this website to help get these operators straight and imagine what these streams of events look like: https://rxmarbles.com/#map (EDIT: I now think this post is far better: https://indepth.dev/learn-to-combine-rxjs-sequences-with-super-intuitive-interactive-diagrams/)

EDIT: Because I was watching a FormArray that could be modified by the user via the add/delete buttons, I added a changesUnsubscribe subject and reference that in the takeUntil. This allows me to discard the old set of watches and setup new ones when the list changes. So now I call watchForChanges() when items are added or removed from the list.

changesUnsubscribe = new Subject(); ... watchForChanges() {   // cleanup any prior subscriptions before re-establishing new ones   this.changesUnsubscribe.next();    merge(...this.formArray.controls.map((control: AbstractControl, index: number) =>             control.valueChanges.pipe(                 takeUntil(this.changesUnsubscribe),                 map(value => ({ rowIndex: index, control: control, data: value })))     )).subscribe(changes => {             this.onValueChanged(changes);     }); } 
like image 88
flyer Avatar answered Sep 20 '22 21:09

flyer