Reputation: 3651
on loading of my parent component, i am updating the form of that (parent) component by using patchValue.
on of that form components i my child ControlValueAccessor component that also has a formgroup. therefore, on my writeValue function i am updating the child component regarding the value that passed from the parent component patchValue.
my writeValue function looks like that:
writeValue(value: CountryAndCity): void {
if (value != null && value.country && value.city) {
this.isDefaultValueDefined = true;
this.form.patchValue({country: value.country});
this.form.patchValue({city: value.city});
**this.cdRef.detectChanges();**
}
}
As you can see, i had to use detectChanges() as the last line of the above function because without it i am getting the error:
ParentComponent.html:1 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-pristine: true'. Current value: 'ng-pristine: false'.
i think i understand why is that. Angular start the check of the parent component, then, start to check the child component but then, on the child component the child component, on the writeValue function changing the parent component form state.
i feel uncomfortable with the use of detectChanges(). is there a way to do something else? is it a must when using parent and child components that each of them has formGroup and the child component is one of the parent formgroup formControl?
Upvotes: 13
Views: 6319
Reputation: 23803
Your approach is good and @Tobi's answer is good too!
That said, if you do not want to struggle too much in building nested forms (using sub components)/creating custom control value accessors you should definitely checkout this library: https://github.com/cloudnc/ngx-sub-form
The error you encountered should never happen if you use the library as we handle that case pretty much like Tobi did.
From your point of view it'll just require you to extend a class and not much more! Will also give you type safety, access to nested errors, etc.
I've also replied to a question on how to build complex/nested/sub forms, this might be of interest for you https://stackoverflow.com/a/56375605/2398593
Edit:
If you want to go further, I've just published a blog post to explain a lot of things about forms and ngx-sub-form here https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9
Upvotes: 0
Reputation: 2040
I don't think, that this is happening because of your write
function but rather that something in your form is emitting values right after the child has been created, but it is hard to tell without seeing more of your code.
I had the same problem so what I did to prevent this error without manually triggering change detection was to decouple the value propagation to the parent from the current change detection cycle.
An easy way to achieve this is to yield
in the value upstream like
this.form
.valueChanges
.pipe(
delay(0)
)
.subscribe(it => {
this.propagateChange(...)
})
I do not know if this is the best approach but for me it works.
BTW: I would also recommend using {emitEvent: false}
as patch options, which prevents the child from firing the event in the write
function. This would solve your problem if your assumption about the source of the error is true, but wouldn't if my assumption was true.
Upvotes: 2
Reputation: 425
Angular application state change could comes from :
But when you are writing your value with your Control Value Accessor you need to tell angular update the changes with ChangeDetectorRef
and detectChanges()
You should have a look at this part Who notifies Angular of article from Pascal Precht
Upvotes: 0