I have implemented the following component. It works and behaves as expected. Nevertheless, as the implementation of ControlValueAccessor
was new to me, I had to follow a blog without understanding the deeper details of a few sections. So this is a type of "it works but why?!" situation.
@Component({ selector: ..., templateUrl: ..., styleUrls: ...,
providers: [{ provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputTextComponent),
multi: true }]
})
export class InputComponent implements ControlValueAccessor {
constructor() { }
@Input() info: string;
onChange: any = () => { }
onTouch: any = () => { }
writeValue(input: any): void {
this.info.value = input;
// this.onChange(input);
// this.onTouch();
}
registerOnChange(_: any): void { this.onChange = _; }
registerOnTouched(_: any): void { this.onTouch = _; }
setDisabledState?(state: boolean): void { }
onEdit(value: string): void { this.onChange(value); }
}
When I've got it working, I commented out the second and third line of writeValue(...)
method and, as far I can tell, nothing broke. Those calls are consistently suggested by other blogs as well, so I'm convinced that it's improper to omit them. However, I don't believe in magic and prefer to have a concrete reason for doing things.
Why is it important to execute the call to onChange(...)
and onTouch(...)
in writeValue(...)
? What will go wrong and under what circumstances can it be expected?
As a side quest, I also tried to comment out the other methods and discovered that I couldn't tell anything going bananas when I removed setDisabledState(...)
. When can that one be expected to cause problems? Does it really need to be implemented (I've seen version with question mark both before and after the parentheses with parameters like so: setDisabledState?(state: boolean): void { }
but also like this: setDisabledState(state: boolean)?: void { }
).
The writeValue method is used by formControl to set the value to the native form control. The registerOnChange method is used by formControl... Read more > Implementing Control Value Accessor in Angular | by Majd Asab.
ControlValueAccessorlink. Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.
The registerOnChange method is used by formControl to register a callback that is expected to be triggered every time the native form control is updated.
The CVA interface is a bridge between FormControls and their elements in the DOM. A component extending the CVA interface can create a custom form control that behaves the same as a regular input or radio button.
Read this article that explains the ControlValueAccessor
in great details:
You usually need to implement ControlValueAcceessor
interface on a component if it's supposed to be used as part of an Angular form.
I commented out the second and third line of writeValue(...) method and, as far I can tell, nothing broke.
This is probably because you're not applying any form directive - formControl
or ngModel
that links a FormControl
to your custom input component. The FormControl
uses writeValue
method of the InputComponent
for communication.
Here is the picture from the article I referenced above:
The writeValue
method is used by formControl
to set value to the native form control. The registerOnChange
method is used by formControl
to register a callback that is expected to be triggered every time the native form control is updated. The registerOnTouched
method is used to indicate that a user interacted with a control.
Why is it important to execute the call to onChange(...) and onTouch(...) in writeValue(...)? What will go wrong and under what circumstances can it be expected?
This is the mechanism by which you custom control that implements ControlValueAcceessor
notifies the Angular's FormControl
that a value in the input has changed or the user interacted with the control.
...discovered that I couldn't tell anything going bananas when I removed setDisabledState(...)...Does it really need to be implemented?
As specified in the interface this function is called by the forms API when the control status changes to or from "DISABLED". Depending on the value, it should enable or disable the appropriate DOM element.
You need to implement it if you want to be notified whenever the status of an associated FormControl
becomes disabled
and then you can perform some custom logic (for example, disable your input component).
I don't think the accepted answer answers the most central question in a succinct way:
Why is it important to execute the call to onChange(...) and onTouch(...) in writeValue(...)?
It isn't. You do not need to call onChange in writeValue. It is not the intended usage (see documentation link below).
What will go wrong and under what circumstances can it be expected?
Expect nothing to go wrong. Elaborate answer:
If you were to call onChange
from inside writeValue
you'll notice:
registerOnChange
not called) when writeValue is first called.emitModelToViewChange = false
to avoid recursively calling writeValue) with the value you gave it - which you just received - from your model.In other words, unless you rely on your component to somehow immediately change the value it receives in writeValue, and for it to pass those changes back to your model but only on second and later invocations of writeValue, you're safe to not call onChange from writeValue.
See example in documentation here: https://angular.io/api/forms/ControlValueAccessor
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With