Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 512
TSG Avatar asked Oct 29 '22 02:10

TSG


1 Answers

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.)...

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.changeDetectorRef.detectChanges();
    }
}
like image 163
A. Tim Avatar answered Jan 02 '23 21:01

A. Tim