crooksey
crooksey

Reputation: 8809

Am I using FormArray correctly?

So I have code that compiles without error, but then when using I am given an error...

My form component:

export class OrderHeaderComponent implements OnInit {
    orderForm: FormGroup;
    orderLines: FormArray;

    ngOnInit() {
        // build the form model
        this.orderLines = this.fb.array([])
        this.orderForm = this.fb.group({

            orderHeadForm: this.fb.group({ // create nested formgroup to pass to child
                selectTypeahead: ['', 
                                    Validators.required],
                ohReference: ['', 
                                    Validators.required],
                }),

            orderLines: this.orderLines,

        })

    }

    someFunction(){
        this.orderLines.push(this.fb.group({
                    ['newInputName']: ['', 
                                    Validators.required],
                    }));
    }
}

Now this is a parent component, that passes the form to various children (this works minus the formArray part which I am currently working on). Each child looks something like this: parent_template:

<form [formGroup]="orderForm" (ngSubmit)="orderFormSubmit()">
    <childTemplate [orderHeadForm]="orderForm.controls.orderHeadForm">
    </childTemplatet>
</form>

Child Template:

<div class="form-group" [formGroup]="orderHeadForm">
        <label for="oh-custaccount">Customer Account #</label>

    <input class="form-control" type="text" 
    formControlName="selectTypeahead"
    (focusout)=lookupCustomerAccountReactive() />

    <p *ngIf="orderHeadForm.controls.selectTypeahead.errors?.required" 
    style="color:red;">Number required!</p>
</div>

Child Component:

export class CustomerSelect implements OnInit {

    @Input() orderHeadForm: FormGroup;

....
}

Now at this point, I am not even trying to render new form inputs from the formArray, I simply want to have them created without error, so I can then build the templates after. But currently my app crashes as soon as I call someFunction() to add a new item to the orderLines array:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '{
"orderHeadForm": {
    "selectTypeahead": "",
    "ohReference": ""
},
"orderLines": []
}'. Current value: '{
"orderHeadForm": {
    "selectTypeahead": "",
    "ohReference": ""
},
"orderLines": [
    {
    "newInputName": ""
    }
]
}'

Now I have seen this thread:

how to access the index value of formarray of angular, when nested formControls are placed in a separate component?

But I if I have say 30 child components that all need this, maybe I am doing something wrong with my form setup/construction. Am I doing something wrong/using form array incorrectly? As there is no mention of a fix as mentioned in the documentation, and I doubt my use case is unique.

Upvotes: 3

Views: 1558

Answers (1)

AVJT82
AVJT82

Reputation: 73357

For some reason there is a problem with change detection when you are running the function someFunction. You were wondering if you need to add manual change detection in each child, per the link you shared. But that is not necessary in this case. Why?

The reasong is, that the error is not thrown when @Input is set. That works fine and not throwing error. The error happens when you are calling the function someFunction in parent, which shows us that the issue with change detection is at that point.

By triggering manual change detection after the line:

this.orderLines.push(this.fb.group({
   ['newInputName']: ['', Validators.required],
}));

should therefore be enough and not needing to trigger change detection in each child. So your code should look like this:

import {ChangeDetectorRef} from '@angular/core'

constructor(private ref: ChangeDetectorRef) { }

someFunction(){
  this.orderLines.push(this.fb.group({
     ['newInputName']: ['', Validators.required],
  }));
  this.ref.detectChanges();
}

Upvotes: 5

Related Questions