Reputation: 4890
I've implemented ControlValueAccessor as seen in a tutorial I am following. However, it appears that the component where I am implementing the controlvalueaccessor is not detecting any changes to the ngModel. Did I miss something?
import { Component, OnInit, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CounterComponent),
multi: true
}
]
})
export class CounterComponent implements OnInit, ControlValueAccessor {
constructor() { }
counterValue = 0;
writeValue(value: any) {
console.log('writeValue: ', value);
}
registerOnChange(fn: any) {
console.log('on change: ', fn);
}
registerOnTouched(fn: any) {
console.log('on touch: ', fn);
}
increment() {
this.counterValue++;
}
decrement() {
this.counterValue--;
}
ngOnInit() {
}
}
template:
<button (click)="increment()">Increment</button>
{{ counterValue }}
<button (click)="decrement()">Decrement</button>
and:
<app-counter name="counter" [(ngModel)]="counter"></app-counter>
<p>ngModel: {{ counter }}</p>
At this point in the tutorial, incrementing/decrementing the counter should be triggering the controlvalueaccessor methods I have defined but it isn't.
Upvotes: 5
Views: 2981
Reputation: 10738
You must register the change. Do something like that :
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-counter',
template: `
<button (click)="increment()">Increment</button>
{{ counterValue }}
<button (click)="decrement()">Decrement</button>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CounterComponent),
multi: true
}
]
})
export class CounterComponent implements ControlValueAccessor {
constructor() { }
counterValue = 0;
writeValue(value: any) {
this.counterValue = value;
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
private onChangeCallback: (_: any) => void = () => {};
registerOnTouched(fn: any) {
console.log('on touch: ', fn);
}
increment() {
this.counterValue++;
this.onChangeCallback(this.counterValue);
}
decrement() {
this.counterValue--;
this.onChangeCallback(this.counterValue);
}
}
Or a more elegant version using setter:
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-counter',
template: `
<button (click)="increment()">Increment</button>
{{ counterValue }}
<button (click)="decrement()">Decrement</button>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CounterComponent),
multi: true
}
]
})
export class CounterComponent implements ControlValueAccessor {
private _counterValue = 0;
get counterValue(): number {
return this._counterValue;
}
set counterValue(value: number) {
this._counterValue = value;
this.onChangeCallback(value);
}
writeValue(value: any) {
this.counterValue = value;
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
private onChangeCallback: (_: any) => void = () => {};
registerOnTouched(fn: any) {
console.log('on touch: ', fn);
}
increment() {
this._counterValue++;
}
decrement() {
this._counterValue--;
}
}
PS: Don't forget to update your internal model with incoming values in writeValue()
as shown in both examples.
Upvotes: 4