Home > Software engineering >  Is is OK to use setTimeout to avoid NG0100 when doing patchValue to a reactive form in afterViewInit
Is is OK to use setTimeout to avoid NG0100 when doing patchValue to a reactive form in afterViewInit

Time:09-17

I have a parent - child component with reactive forms. Child propagates changes to parent via ControlValueAccessor.

I need to pass initial value via the template with @Input annotated variable (in the real app I want to pass the initial value from ngFor hence the @Input).

I can't set the passed in initial value during formGroup creation in the child constructor because the @Input ed value is not available yet.

I also can't set it in ngOnInit for the same reason.

In afterViewInit I can do it, and it's working, BUT only if I wrap the patchValue call inside a setTimeout. Otherwise NG0100: Expression has changed after it was checked error is thrown:

ngAfterViewInit() {
  console.log('ngAfterViewInit called');
  // if I omit setTimeout then NG0100: ExpressionChangedAfterItHasBeenCheckedError
  // is it OK for using setTimeout for this?
  setTimeout(() => {
    this.formGroup.patchValue({
      firstName: this.initialFirstName,
    });
  }, 0);
}

Is it OK to use setTimeout to avoid the error, is it reliable? Is there a better solution?

The whole thing is here: https://stackblitz.com/edit/angular-ivy-rohjor?file=src/app/name-input/name-input.component.ts

CodePudding user response:

It would have worked inside ngOnInit, but the problem is that the change handler of your control value accessor is not yet registered. You can do it inside registerOnChange handler like this:

public registerOnChange(fn: any): void {
  console.log('registerOnChange called');
  this.onChange = fn;

  this.setUpDefaultValues();
}

private setUpDefaultValues(): void {
  this.formGroup.patchValue({
    firstName: this.initialFirstName,
  });
}

This way, when your subscribe handler inside the ngOnInit method is hit, the this.onChange will have the correct value (the one that was registered through the control value accessor).

I recommend you to stop giving default values to these control value accessor handlers because this way, you will actually get more meaningful errors (like Error: this.onChange is not a function) that should trigger this alarm for you and let you know that you try to use the handler before it was registered:

private onChange?: (value: any | null) => void;
  • Related