Search code examples
angulartypescriptangular-reactive-formsangular-forms

Reactive Forms Not Working with ControlValueAccessor


I have built a control called TextBox, and for simplicity, I will only post the relevant parts of the control on here.

    @Component({
      selector: 'app-textbox',
      template:
      `
        <input [(ngModel)]="value" [disabled]="disabled" />
      `,
      styleUrls: ['./textbox.component.css']
    })
    export class TextboxComponent implements OnInit, ControlValueAccessor {
    
      constructor() { }
      writeValue(obj: any): void {
        this._value = obj;
      }
      registerOnChange(fn: any): void {
        this.onChange = fn;
      }
      registerOnTouched(fn: any): void {
        this.onTouch = fn;
      }
      setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
      }
    
      disabled = false;
    
      onChange:()=>{}
      onTouch:()=>{};
    
      private _value:string;
      public get value():string {
        return this._value
      } 
      public set value(value:string){
        this._value = value;
      }
    
      ngOnInit(): void {
      }

and my app.component.ts looks like:

    @Component({
      selector: 'app-root',
      template:
      `
        <form [formGroup]="form" novalidate>
          <div>
            <label >Name</label>
            <app-textbox formControlName="name"></app-textbox>
          </div>
        </form>
      `,
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit{
      /**
       *
       */
      constructor(private formBuilder:FormBuilder) {
      }
    
      form = this.formBuilder.group({
        name:['', Validators.required]
      })
    
      model:NameModel = {
        name:'test'
      }
    
      ngOnInit(): void {
        this.form.get('name').setValue(this.model.name);
      }
    }
    
    interface NameModel{
      name:string;
    }

When I run the application I would expect that the textbox would be populated with the value test.

Can someone please explain why it is not?

I will add when I do this.form.get('name')?.value I get the correct value.


Solution

  • You just have to move setting the value this.form.get('name').setValue(this.model.name); to ngAfterViewInit instead of ngOnInit, or you have to call after it:

    this.form.updateValueAndValidity();
    

    Btw, there are two notes here you should take care of them:

    1. You missed writing the value back to the form-control after it has been changed in your TextboxComponent, so you have to call the registered onChange method within the value setter like the following:
    private _value: string;
    public get value(): string {
      return this._value;
    }
    public set value(value: string) {
      this._value = value;
      this.onChange(value);
    }
    
    1. It's better in your case to initialize the FormGroup within the ngOnInit, and set the default value of name directly, like the following:
    form: FormGroup;
    
    ngOnInit(): void {
      this.form = this.formBuilder.group({
        name: [this.model.name, Validators.required],
      });
    }