Angular Custom Form Control — Simple Color Picker

Nichola Alkhouri
Jan 22 · 6 min read

In this story, I will show you how to create a custom form control that will integrate with Angular forms API and can be used in both template-driven and model-driven form the same way any native form control is used(e.g <input type=”text” ../>).

For this purpose we will create a simple color picker component and turn it into a valid reusable form control, after we complete this implementation, you will be able to use your newly created form control in any template-driven or model-driven forms as the following:

Template Driven Approach:

<color-picker [(ngModel)]=”color”></color-picker>

Model-driven approach (reactive forms):

<color-picker [formControl]=”color”></color-picker>

<color-picker formControlName=”color”></color-picker>

You can find the full source code in this Blitz, or embedded at the end of the Article

Let's start by creating our component as the following:

A very basic component:

  • We have a list of predefined colors named colors, we iterate over each one of these colors in the view and render a div with a background of that specific color.

Simple but yet not useful in the contexts of a form, there is no way this component can communicate with the surrounding form to inform it of any change in the selected color, and vice-versa there is no way for the form to pass the component a specific color to be selected.

To fix the above communication problems, let's turn this component into a valid angular form control, to do so we have to do two things:

1- We need our component to Act as angular forms API expect it to. To do so we need to implement the ControlValueAccessor interface in the new component.

2- We need to make our component Visible to angular forms API, and we do so by providing our component using the NG_VALUE_ACCESSOR injection token.

1- Implementing ControlValueAccessor Interface

To enable angular forms API to interact with our component, we need to implement the ControlValueAccessor interface, If you take a look at Angular code you can find this as a description of theControlValueAccessor interface:

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

* Implement this interface to create a custom form control directive * that integrates with Angular forms.

This interface consists of the following methods which will implement each of them in our component:

  • WriteValue: the forms API calls this method whenever the value of the model linked to this control is changed programmatically. so basically this is how Angular telling our component that somehow the value of the form has been changed and we need to react to this change in our component. The method provides us with the new value in its only parameter obj, and we need to update the UI accordingly, here we only need to assign the new value to the selectedColor property of the color picker component.
writeValue(obj: any): void {  this.selectedColor = obj;}
  • registerOnChange: this method provides us with a way to communicate in the opposite direction, as we saw WriteValue will notify our component of the changes from the outer form, now we need a way to notify the outer form of the changes from inside our component UI (in our case user clicking on a new color). This method provides us in its parameter with a callback function fn that we should call whenever the value is changed in the UI, to do so we need to save the callback function in a variable, and we use it whenever the user clicks on a new color.
private _onChange: any;registerOnChange(fn: any): void {    this._onChange = fn; // Save the callback function
}
colorClicked(color: string) { this.selectedColor = color; this._onChange(this.selectedColor); // Call the saved callback}
  • registerOnTouched: this method is similar to registerOnChange, it provides us with a callback function to notify the form when the current form controlled is touched, usually, when using an input field, we call the callback on blur, in our example, we consider that the control has been touched once we select any new color.
private _onTouch: any;registerOnTouched(fn: any): void {    this._onTouch = fn;   // Save the callback function}
colorClicked(color: string) {
this.selectedColor = color; this._onTouch(true); // Call the saved callback}
  • setDisabledState: the last method to implement, the forms API will call this method whenever the status of the control changes from or to disabled, we are expected to interact on this change and to disable the selection of colors in our component, so we will always save the value returned from this method.
private _isDisabled: boolean;setDisabledState?(isDisabled: boolean): void {    this._isDisabled = isDisabled;}

2- Providing our component using the NG_VALUE_ACCESSOR injection token

So far our new component is ready to integrate with Angular forms API, however, one more step is still necessary to allow forms API to recognize our component as a valid form control and interact with it (this interaction is possible because we implemented ControlValueAccessor interface in the previous step).

Before we start let’s take a look at the source code of Angular official FormControlDirective which is responsible for linking our component with the form, and try to understand how this directive builds this link, by looking at the constructor of that directive we find the following:

constructor( ...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],
...) {...
selectValueAccessor(this, valueAccessors);
}

Notice the directive is injecting a token NG_VALUE_ACCESSOR and expect it to provide a list of ControlValueAccessor (the interface we have just implemented). then this value is being saved and used internally.

What does this mean to us? this means that if we wanted FormControlDirective to recognize our component and interact with it, we need to provide our component using the injection token NG_VALUE_ACCESSOR and to do so we just need to update the options of the Component decorator to be as the following:

  • We configure the component injector using the injection token NG_VALUE_ACCESSOR .

Now our component is ready to be used in any template or model-driven form, we can use it for an example in our AppComponent as the following:

  • We define a formGroup with two controls title and color, and we add an HTML form element with the directive formGroup

The final result will be as the following after adding some styling:

Thank you for reading! and remember, never stop learning :)

The Startup

Get smarter at building your thing. Join The Startup’s +800K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store