Why is this.value undefined/null on every lifecycle hook when using [ngModel]="true"

1.4k Views Asked by At

I have a custom form control which implements ControlValueAccessor to handle [(formControl)]="..." and [(ngModel)]="...".

@Component({
  selector: 'app-foo',
  templateUrl: './foo.component.html',
  styleUrls: ['./foo.component.css'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => FooComponent),
            multi: true,
        },
    ],
})
export class FooComponent implements ControlValueAccessor, OnInit, AfterContentInit, OnInit, OnChanges, DoCheck, AfterContentChecked, AfterViewInit, AfterViewChecked {

  constructor() { }

  ngOnChanges() {
    console.log('ngOnChanges', this.value);
  }
  ngOnInit() {
    console.log('ngOnInit', this.value);
  }
  ngDoCheck() {
    console.log('ngDoCheck', this.value);
  }
  ngAfterContentInit() : void {
    console.log('ngAfterContentInit', this.value);
  }
  ngAfterContentChecked() : void {
    console.log('ngAfterContentChecked', this.value);
  }
  ngAfterViewInit() : void {
    console.log('ngAfterViewInit', this.value);
  }
  ngAfterViewChecked() : void {
    console.log('ngAfterViewChecked', this.value);
  }

  /**
   * Write a new value to the element.
   */
  public writeValue(value : any) : void { // tslint:disable-line:no-any
    this._value = value;
    console.log('writeValue', this.value);
  }

  ...

I use this component like this:

<app-foo [ngModel]="true"></app-foo>

I assumed the value should be defined at least in ngAfterContentInit() but it is always undefined or null. At least in the first run. Here are my console logs:

ngOnInit undefined
ngDoCheck undefined
writeValue null
ngAfterContentInit null
ngAfterContentChecked null
ngAfterViewInit null
ngAfterViewChecked null
---
writeValue true
ngDoCheck true
ngAfterContentChecked true
ngAfterViewChecked true

Why is value always undefined/null and is there a way to change that?

1

There are 1 best solutions below

3
On

You will need to implement ControlValueAccessor in order to make it work :

export class FooComponent implements ControlValueAccessor {
  writeValue(value) {
   this._value = value;
  }
  ...
}

The writeValue function will be executed when the input value of ngModel change. You will also have to implement registerOnTouch and registerOnChange.

Have a look at this alligator.io article for a more information.