2

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>

2 Answers 2

3

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';
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for that...So it does look like two-way binding using ngModel is not supported out of the box for custom components (i.e. non input elements) in versions >= 2 and need some sort of custom code to kind of achieve that for custom components...Would that be correct statement ?
Lil bit correct.. because ControlValueAccessor is base class used by angular too for data binding, so component is custom that's why we have to extends our class in order to angular compiler detects it ngModel mechanism with our custom component.. we are going to decide what to do with component. That's it. Else angular provides us DI and we extends and pass our component to DI. Using providers array in component.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.