2

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.

Maciej Treder
  • 11,866
  • 5
  • 51
  • 74
crooksey
  • 8,521
  • 6
  • 30
  • 52
  • @AJT_82 It's the "Am I using this correctly?" that threw me, and the fact that the error was in with the JSON. Retracted. I need to read these more thoroughly before making my decisions. Coffee... – R. Richards May 24 '17 at 12:55
  • 3
    aah, deleted my answer since I found out why it threw an error. There are tho mistakes in your code, have you checked that, are they in your actual code, e.g `['newInputName]` is missing an `'`. I don't understand why you'd get that error tho. But try and trigger change detection manually like in the answer you have linked to. Do that after adding the new formgroup in `orderLines`. Best would be tho, if you could try and recreate the issue in a plunker :) – AT82 May 24 '17 at 12:55
  • @R.Richards Haha, no worries, happens to the best of us, at least to me many times :P – AT82 May 24 '17 at 12:56
  • @AJT_82 that was a typo in my example, I will try trigger change detection after adding a new line – crooksey May 24 '17 at 13:01
  • Thanks @AJT_82 adding ref.detectChanges() after creating the form element worked, I thought I had to do a seperate ngOnChanges(){} for each child with an Input element, if you want to post as an answer, I can accept. – crooksey May 25 '17 at 11:09
  • @crooksey Glad to hear it worked out! :) Undeleted answer and updated it, also with an explanation why not needing to trigger change detection in children :) – AT82 May 25 '17 at 11:23

1 Answers1

4

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();
}
AT82
  • 71,416
  • 24
  • 140
  • 167