ExpressionChangedAfterItHasBeenCheckedError when add validator in ngOnInit
ExpressionChangedAfterItHasBeenCheckedError when add validator in ngOnInit
I am trying to add a custom validator to an input, but when I do so it triggers an ExpressionChangedAfterItHasBeenCheckedError error saying something changed from TRUE to FALSE.
I traced the problem down to the line below:
ngOnInit(): void {
this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));
}
If I remove the TabValidator.ipaddress() then the error disappears. Similarly if my validator forces return of 'null' then the error goes away. My validator is as follows:
private static _ipaddress(address: string): any {
console.log('checking ip address: '+address+' valid: '+ip.isV4Format(address)+' is null: '+(address === null ? 'yes' : 'no'));
if (!ip.isV4Format(address)) { return { 'wrongFormat': true }; }
console.log('returning null');
return null;
}
public static ipaddress(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
console.log('validator returning: '+TabValidator._ipaddress(control.value));
return TabValidator._ipaddress(control.value);
};
}
From the console log it appears that the validator is returning the same value each time.
Can someone explain what is going wrong here? I can't explain what variable is changing from 'true' to 'false' since there are no booleans involved in the lines above. How can the validator return different values without appearing in my console log?
I read one SO questions about not doing things in ngOnInit but that seems like a red herring.
UPDATE: I tried:
ngOnInit(): void {
Promise.resolve().then(() => {
this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));
});
}
and also
ngOnInit(): void {
setTimeout(() => this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()])), 10);
}
but then error changes to:
ERROR Error: Cannot find control with name: 'gateway'
SOLUTION:
I traced this down to a bug - using reactive forms + validators + ngb tabs. Adding the validator to the control on a tab causes the error. Only solution is to change to template forms.
*ngIf
I don't have any ngIf or ngFor. I do apply different styles based on validity classes applied to the input.
– TSG
Sep 7 '17 at 17:32
yes, as I said,
*ngIf* could be an issue, but there are a lot of things that cause that error and as I said, mostly because of the ordering of components rendering. There is too less code to tell you where is the issue. If you can reproduce in a plunker (I recommend nowadays stackblitz.com) would be very helpful...– DAG
Sep 7 '17 at 17:36
*ngIf*
check this article Everything you need to know about the
ExpressionChangedAfterItHasBeenCheckedError error– AngularInDepth.com
Sep 7 '17 at 19:36
ExpressionChangedAfterItHasBeenCheckedError
1 Answer
1
If you look at angular's github Issues page, you can find couple tickets related to same problem you have (as example https://github.com/angular/angular/issues/15634 , there are more, this one I just found as example). Long story short: angular doesn't like view change in some (all?) lifecycle hooks. What exactly changes in your particular case: I assume that is validity state of formField (i.e. internally in Angular's Control code, that you don't have access to), that internally triggers change detection...
If that is possible for you, move this.ipv4Fields.addControl() to constructor. If that is not possible - see suggestions in github tickets (do detectChanges, or Promise, or setTimeout, etc.)...
this.ipv4Fields.addControl()
detectChanges
Promise
setTimeout
UPDATE:
@Component(){}
export class MyComponent implements OnInit{
@Input()
ipv4Fields: FormGroup;
//inject `ChangeDetecorRef`
constructor(protected changeDetectorRef: ChangeDetectorRef){
}
ngOnInit(){
this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));
this.chageDetectorRef.detectChanges();
}
}
I tried to move to constructor but fields weren't created yet so it errored. I tried using promise (see question update above) but didn't work. Can you provide a small code example on what my ngOnInit should look like?
– TSG
Sep 7 '17 at 17:22
Personally, I'm so tired fighting with that error, so I each time I face that problem, I just do
this.changeDetectorRef.detectChanges();. Each angular release they fix some of those problems, so I retest my code without that part. So controller(protected changeDetectorRef: ChangeDetectorRef){} ngOnInit(){/*other code;*/ this.changeDetectorRef.detectChanges();}. I had similar problem, when I need to update validator dynamically. My form was in modal. So instead of setting validator in ngOnInit I moved that code to modalShow() callback and that was enough %)– A. Tim
Sep 7 '17 at 17:33
this.changeDetectorRef.detectChanges();
controller(protected changeDetectorRef: ChangeDetectorRef){} ngOnInit(){/*other code;*/ this.changeDetectorRef.detectChanges();}
ngOnInit
modalShow()
Ok I implemented the above (I think controller should be constructor) but still get the ExpressionChanged... error
– TSG
Sep 7 '17 at 17:40
sorry then, I don't have better solution now :(
– A. Tim
Sep 7 '17 at 17:45
I appreciate the ideas. Does that mean my problem is different from the bug you mentioned, or that mine is just a different manifestation of the same bug? (guessing)
– TSG
Sep 7 '17 at 17:52
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
this mostly happens when something is not correct with the rendering order of your components. something in your lifecycle is out of order. there are a lot of threads on angular github issues page about this issue. review your components. verify if you don't have any
*ngIfblocking the rendering of some component and so on...I ran into this problem several times and it was always because something was not correct with my code / components rendering order.– DAG
Sep 7 '17 at 17:29