'Two-way data binding in Angular

My question is referring to all Angular versions >= 2

So for two-way data binding, does Angular support it out of the box using ngModel. Is it only supported for form controls e.g. input box ?

Is the out of box support not available for custom components ? e.g. can we not use ngModel directly as below for a custom component ? Or would that need custom code ?

<custom-counter [(ngModel)]="someValue"></custom-counter>


Solution 1:[1]

You can find demo here in this stackblitz link

For your any custom-component if you need to use [(ngModel)] then you need to use your custom ControlValueAccessor base class.

Create one class called AbstractValueAccessor...

import { forwardRef } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";

const noop = () => {};
export abstract class AbstractValueAccessor implements ControlValueAccessor {
  //The internal data model
  private innerValue: any = "";

  //Placeholders for the callbacks which are later provided
  //by the Control Value Accessor

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

  //get accessor
  get value(): any {
    return this.innerValue;
  }

  //set accessor including call the onchange callback
  set value(v: any) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  //Set touched on blur
  onBlur() {
    this.onTouchedCallback();
  }

  //From ControlValueAccessor interface
  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

  //From ControlValueAccessor interface
  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  //From ControlValueAccessor interface
  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }
}

export function MakeProvider(type: any) {
  return {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => type),
    multi: true,
  };
}

Now, You need to use above class in you custom-component.

custom-component

import { Component, OnInit } from "@angular/core";
import {
  AbstractValueAccessor,
  MakeProvider,
} from "../app/abstract-value-accessor";

@Component({
  selector: "app-custom-input",
  templateUrl: "./custom-input.component.html",
  styleUrls: ["./custom-input.component.css"],
  providers: [MakeProvider(CustomInputComponent)],
})
export class CustomInputComponent
  extends AbstractValueAccessor
  implements OnInit
{
  ngOnInit() {}
}

above, providers: [MakeProvider(CustomInputComponent)] this line we provide our custom value accessor to our component. Now , we are ready to use this component.

app-component.html

<app-custom-input [(ngModel)]="customValue"></app-custom-input>

app-component.ts

customValue = 'custom-value';

Solution 2:[2]

Angular provides ControlValueAccessor to have control over custom components.

ControlValueAccessor interface gives the power to leverage the Angular forms API, and create a connection between it and the DOM element.

Here is the example that implements this concept:

https://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 lilalinux
Solution 2 Afsar