'valueChange in formArray inside a formArray

i have a form that contains a formArray (FinYearArray) that on his turn contains another formArray (RecipientArray) both these FinYearArray and RecipientArray contains a formControl for the amount, everytime there is a change on these fields the total should be calculated for the financial years and for the recipient countries by financial year. For the total of the recipients i created a formControl in the FinYearArray named 'RecipientTotal'

//structure of the form
{
    "FinYearArray": [
        {
            "FinYear": 2022,
            "FinYearAmount": 0,
            "RecipientTotal": 0,
            "RecipientArray": [
                {
                    "CtryDesc": "",
                    "Amount": 0,
                },
                {
                    "CtryDesc": "",
                    "Amount": 0,
                }
            ],
        },
        {
            "FinYear": 2023,
            "FinYearAmount": 0,
            "RecipientTotal": 0,
            "RecipientArray": [
                {
                    "CtryDesc": "",
                    "Amount": 0,
                }
            ],
        }
     ],
    "ProjectCode": "",
}

in my ngOnInit() i have this code to check when there are changes in the FinYearArray formArray and automaticcaly also in the RecipientArray formArray because it is inside the other formArray

//function that calculates the totals for fin year and recipients/fin year

    this.formDetailReport.get('FinYearArray').valueChanges.subscribe(values => {
        this.myFinYearTotal = 0;
        const ctrl = <FormArray>this.formDetailReport.controls['FinYearArray'];

    //loop trough fin years
        ctrl.controls.forEach(x => {
            let parsed = Number(x.get('FinYearAmount').value);
            this.myFinYearTotal += parsed;
        });

//loop through recipient countries for each financial year
            for (let i = 0; i < ctrl.controls.length; i++) {
                this.myRecipientTotal = 0;
                const recipientFormArray = <FormArray>ctrl.controls[i].get('RecipientArray');

                recipientFormArray.controls.forEach(x => {
                    let parsed = Number(x.get('Amount').value);
                    this.myRecipientTotal += parsed;
                });
                console.log('total : ' + i + ' amount : ' + this.myRecipientTotal );
// when using this code i get the error at the end of this mail
                this.formDetailReport.controls['cFinYearGroup']['controls'][i].controls.cRecipientTotal.setValue(this.myRecipientTotal);
                // ctrl[i].controls.cRecipientTotal.setValue(this.myRecipientTotal);
            }
            this.ref.detectChanges();
        });

but i get this error

// error when i try to assign the calculate value to the formControl
core.js:4442 ERROR RangeError: Maximum call stack size exceeded
    at EuiInputNumberComponent.ngDoCheck (eui-components-next.js:11713:1)
    at callHook (core.js:3285:1)
    at callHooks (core.js:3251:1)
    at executeInitAndCheckHooks (core.js:3203:1)
    at refreshView (core.js:7395:1)
    at refreshEmbeddedViews (core.js:8481:1)
    at refreshView (core.js:7404:1)
    at refreshComponent (core.js:8527:1)
    at refreshChildComponents (core.js:7186:1)
    at refreshView (core.js:7430:1)

i think this comes because every time i update the recipient total (cRecipientTotal formControl) this piece of code is re-executed because there is a valueChange inside the formArray

isn't there a way that this valueChange is only watching the FinYearAmount formControl inside the formArray instead of watching all the fields of this formArray? and afterwords watching the formcontrol Amount in the RecipientArray



Solution 1:[1]

When updating an AbstractControl which has a valueChanges listener attached and you don't want to retrigger it (causing the infinite loop you are suffering from), you can add a configuration object to your setValue/patchValue call:

myFormControl.setValue(value, { emitEvent: false });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Will Alexander