Angular Custom Form Controls With Control Value Accessor

Dilina Hirantha
JavaScript in Plain English
6 min readJul 3, 2021

--

In the development of complex Angular applications, developers tend to build large components that encompass the entire form and can make it challenging to re-use in efficient manner. In that situation the concept reusable/custom components come to forward. Custom form controls are a typical pattern in complex Angular applications. It’s common to want to encapsulate HTML, CSS, and accessibility in an input component to make it easier to use in forms throughout the application.

In this article, we are going to explore the usage of the Angular custom components with reactive forms. For that purpose, we are going to observe the inbuilt Angular directive “control value accessor”.

Control Value Accessor Directives

Control Value Accessor is an inbuilt directive in Angular which will be responsible for tracking the value of the child field, and communicate it back to the parent form.

In the below example, we are going to build a custom numeric value counter which contains two buttons to increment or decrement the value. Also, we provide min and max value ranges for this counter where it’s valid.

First of all, we create a new Angular custom component named “custom counter” which acts as the parent component for this practice. Here is the implemented custom component.

In the above snippet, you can see we have implemented onAdd and onRemove methods to increment and decrement the counter value. Also, the template of the custom counter component is shown below.

The next step is to implement the child component which utilizes the custom counter component. We named the child component as quantity form and added built-in validators to add the min and max range values.

The template of the quantity form component as below,

Understanding Control Value Accessor Interface

Still, we haven’t utilized the actual usage of the Control Value Accessor interface. Therefore, the next step is to understand the applicability of the Control Value Accessor interface.

Control Value Accessor interface provides few methods and all those methods are meant to be called only by the Forms module at runtime, and they are meant to facilitate communication between our form control and the parent form.

  • writeValue: Forms module writes a value into a form control using this method.
  • registerOnChange: If a form value changes due to user input, we need to report the value back to the parent form. This is done by calling a callback, that was initially registered with the control using the registerOnChange method.
  • registerOnTouched: When the user first interacts with the form control, the control is considered to have the status touched, which is useful for styling. In order to report to the parent form that the control was touched, we need to use a callback registered using the registerOnToched method.
  • setDisabledState: form controls can be enabled and disabled using the Forms API. This state can be transmitted to the form control via the setDisabledState method.

As the next step, let’s discuss these methods one by one with their applicability.

Implementation of writeValue

This method is used in the Angular forms module whenever the parent component needs to set the value to the child control. We can do it as shown below.

Implementation of registerOnChange

Using the writeValue method, the parent component can set the value to the child control. But the critical situation is, when the counter value change by incrementing or decrementing, parent component need to aware of it to grab the changing values.

There, the child component can notify the parent about that new value using a callback function. For this purpose, the parent component needs to register the callback function using the registerOnChange method as below.

When the registerOnChange method is called, the changed value will save into a onChange variable. In this implementation, we have declared the onChange variable as a function and initially, it has an empty body. This way, if the program for some reason calls the function before the registerOnChange call was made, it won't run into any errors.

When we increment or decrement the counter value, then we can notify the parent component by calling the callback function as below.

Implementation of registerOnTouched

When the form is initialized, form controls are considered to be in the untouched status and the ng-untouched CSS class is applied to the form group and also to each of its individual child controls.

In the previous topics, we set and grab the value changes to the parent component. Also, we need to notify the form control has been touched by the user to the parent component. Therefore, we use the registerOnTouched method.

If the user does interact with the form control, then that implies the ng-untouched CSS class is going to be applied to the form control.

First of all, we need to register the callback function as the previous function as below.

Next, we need to call this callback in situations when counter value is incremented or decremented by the user. There, the status of the form control will be changed as the touched.

Implementation of setDisabledState

We can use setDisabledState method to disable or enable the child form control. There we initialize a disable variable as false and change the value of it by calling the setDisabledState method.

Configure ControlValueAccessor Interface

This is the main step of this implementation, which is to register the custom counter component as a known value accessor in the dependency injection system using the ControlValueAccessor Interface.

As the configurations, we provide NG_VALUE_ACCESSOR to register this component by adding to the list of known value accessors. Notice the multi flag set to true, this means that this dependency provides a list of values, and not only one value. Now our custom component is now capable of setting the value of a property in a form. Also our the component is now capable of participating in the form validation process with the in-built required and min, max validators.

Furthermore, what if we need to set custom validation for the control?

If the component needs to have its own built-in validation rules, there we need to implement custom counter class using the Validator interface. This interface contains two methods,

  • validate : This method is used to validate the current value of the form control. This method will be called whenever a new value is reported to the parent form. This method returns null if no errors are found.
  • registerOnValidatorChange: This will register a callback that will allow us to trigger the validation of the custom control on demand. There we don’t need to trigger this method whenever new value input to the control but it is required if the form will decide to recall validations ( something changed outside, form initialized, etc.)

For the explanation, let's think that we have removed the in-built min validator form the control and instead of we implement a custom validator to check the counter value is lower than zero. We can do it as below,

In this code snippet, we have return the null if the counter value is valid, otherwise we return error object. For this purpose we don’t need registerOnValidatorChange method since we use only the current value of the control.

For completion of this implementation, we need to register our component as the NG_VALIDATORS injection.

Now we have built fully functional custom control capable of both setting the value of a form property and participating in the form validation process. Finalized code as below.

Summary

  1. We have implemented a custom component with the Control Value Accessor interface.
  2. Control Value Accessor interface includes three methods as writeValue, registerOnChange, registerOnTouched and setDisabledState.
  3. Among these methods, writeValue method is used to set the value to the child control by the parent component. registerOnChange method is used to communicate between parent and child component when the input value changes. registerOnTouched method is used to notify the parent component whenever child form control is considered to be touched.
  4. Then we implemented custom validation by using the Validator interface.

Thank you for reading! Hope you will use this article to cut out your doubts about Angular custom components.

More content at plainenglish.io

--

--