What is the difference of ControlValueAccessor and banana in a box syntax in implementing two way binding?

Viewed 94

I just found out that there are several ways to implement two way binding in a custom component. What I can't understand is the difference between them. When shoud I prefer using a component which uses banana in a box syntax (with an @input() and @Output() for the model), to implements two way binding like this component

@Component({
  selector: 'app-first-component',
  template: `
    <p>input model value: {{ inputModel }}</p>
    <button (click)="clickHandler()">Clear</button>
  `
})
export class FirstComponentComponent {
  @Input() inputModel: string;
  @Output() inputModelChange = new EventEmitter<string> ();

  clickHandler(): void {
    this.inputModel = '';
    this.inputModelChange.emit(this.inputModel);
  }
}

and one which implements ControlValuAccessor like this

@Component({
  selector: 'app-second-component',
  template: `
    <p>input model value: {{ inputModel }}</p>
    <button (click)="clickHandler()">Clear</button>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SecondComponentComponent),
    multi: true
  }]
})
export class SecondComponentComponent implements ControlValueAccessor {
  private inputModel: string;
  private onChanged: (value: string) => void = () => {};

  clickHandler(): void {
    this.inputModel = '';
    this.onChanged(this.inputModel);
  }

  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }

  writeValue(value: string) {
    this.inputModel = value;
    this.onChanged(this.inputModel);
  }

  registerOnTouched(): void {/* ignored */}
}

I they're used in a parent component like this

<app-first-component [(inputModel)]="value"> </app-first-component>

<app-second-component [(ngModel)]="value"> </app-second-component>

As you can see they are used in the same way and also do the same job. If so, why is there ControlValueAccessor? Is there anything I can do with the second component which is not feasible with the first one?

1 Answers

A ControlValueAccessor (or a component that implements this interface) is used by Angular as a bridge between a native form control (like input, textfield, radio button, etc) and the Angular representation "FormControl"( link in GitHub: https://github.com/angular/angular/blob/b5ab7aff433a67cddaa55e621d17b1a1b07b57c2/packages/forms/src/directives/reactive_directives/form_control_directive.ts).

By implementing the interface of ControlValueAccessor you make sure that Angulars FormControl gets notified when the native form element gets updated and vice versa (when you make changes to the FormControl programmatically, the native UI is updated).

Angular brings along ControlValueAccessors for all known HTML form elements. But when you try to create your own Form Control, which is not build from the native elements (or you want to wrap some UI from third party), then your component should implement ControlValueAccessors interface to make your custom UI form control manageable by Angular forms.

[ngModel] is used in template driven forms to create two way databinding (please notice it is marked deprecated since Angular V6 (see link above). So the whole intend behind the two things is different. From my understanding, [ngModel] works because a working ControlValueAccessors mechanism is already at place.

Related