Search code examples
angularrxjsangular-reactive-forms

How to identify which item in FormArray emitted valueChanges event?


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.
    }
}

Solution

  • I resolved this issue by adding a formControl (named groupIndex) in the formGroup to track the index and subscribing to the valueChanges at the formGroup level instead of formArray level. On valueChanges event, I could then access the formControl that stored the current index.

    this.myMainFormGroup = this.myFormBuilder.group({
      // other formControls
      myFormArray: this.myFormBuilder.array([this.addArrayItem()])
    });
    
    // this method will be called every time the add button is clicked
    addArrayItem(): FormGroup {
      const itemToAdd = this.myFormBuilder.group({
        // dont forget add input control for groupIndex in html for this. It will give error otherwise.
        // i made the input control hidden and readonly
        groupIndex:"", 
        firstName:["", [validator1, validator2]]
        //other formControls
    
      });
    
      const myFormArray = <FormArray>this.myMainForm.get("myFormArray");
    
      //set groupIndex
      itemToAdd.get("groupIndex").patchValue(myFormArray.length -1);
    
      //subscribe to valueChanges
      itemToAdd.valueChanges
        .debounceTime(200)
        .subscribe(data => this.onValueChanged(data));
    
      myFormArray.push(itemToAdd);
    }
    
    onValueChanged(data?: any): void {
      const groupIndex = data["groupIndex"];
    
      const myChangedGroup = <FormArray>this.myMainForm.get("myFormArray").controls[groupIndex];
    
      // now I have hold of the group that changed without having to iterate through the entire array. 
      // run through the custom validator 
      this.generalValidator(myChangedGroup);
    }