Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need call onChange and onTouch in writeValue when implementing ControlValueAccessor in Angular?

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 { }).

like image 976
DonkeyBanana Avatar asked Oct 14 '17 09:10

DonkeyBanana


People also ask

What is writeValue in Angular?

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.

What is use of control value accessor in Angular?

ControlValueAccessorlink. Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.

What is registerOnChange?

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.

What is CVA in Angular?

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.


2 Answers

Read this article that explains the ControlValueAccessor in great details:

  • Never again be confused when implementing ControlValueAccessor in Angular forms

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:

enter image description here

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).

like image 133
Max Koretskyi Avatar answered Sep 20 '22 14:09

Max Koretskyi


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:

  • First onChange call does nothing. That's because onChange callback hasn't been registered (ie registerOnChange not called) when writeValue is first called.
  • Later calls to onChange from inside writeValue updates your model (with 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

like image 39
corolla Avatar answered Sep 17 '22 14:09

corolla