This article has been updated to the latest version of Angular 7. Some content may still be applicable to Angular 2 or other previous versions.

Custom form controls/inputs are a common pattern in complex front-end applications. Its common to want to encapsulate HTML, CSS and accessibility in a component to make it easier to use in forms throughout the application. Common examples of this are datepickers, switches, dropdowns, and typeaheads. All of these types of inputs are not native HTML inputs. We would like them to easily integrate into Angular’s form system to make them easy to use with other form inputs.

In this post, we will show how to create a switch component (app-switch) which is essentially a checkbox with additional CSS and markup to get a physical switch effect. This component will easily integrate into the new Angular Reactive Forms and ngModel. So first let’s take a look at what our final output will look like.

So our switch component is mostly the behavior of a checkbox. It toggles a boolean value in our forms. In this component, we will use a native checkbox and some HTML and CSS to create the switch effect. In Angular, there are two different ways to interact with form controls/inputs. The first recommended default is the Reactive Form API and the other is the NgModel. We will use a special API Angular exposes to allow us to support both API interactions with our custom switch component.

Using Reactive Forms

The Reactive Forms API allows us to create forms in our TypeScript explicitly. This will enable us to keep form logic and validation logic in the TypeScript and our of our templates. With the Reactive Forms API, we use a valid form with a submit event that is triggered by the user. So let’s take a quick look at how a form would look with our custom app-switch component.



<h3>Reactive Forms</h3>
<form [formGroup]="myForm" (submit)="submit()">
  <app-switch formControlName="mySwitch" [label]="'My Switch'" ></app-switch>
  <button>Submit</button>
</form>




import { Component } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  myForm: FormGroup
  
  constructor(private formBuilder: FormBuilder) { }
  
  ngOnInit() {
    this.myForm = this.formBuilder.group({
      mySwitch: [true]
    });
  }
  
  submit() {
    alert(`Value: ${this.myForm.controls.on.value}`);
    console.log(`Value: ${this.myForm.controls.on.value}`);
  }
}


So we can see our custom app-switch work seamlessly with the Reactive Forms/Form Builder API. To learn more about this API check out this post: Angular Form Builder and Validation Management.

Using NgModel

NgModel allows us to bind to an input with traditional two-way data binding similar to Angular 1. While not recommended for forms, this is handy for simple interactions such as toggling the visibility of some UI.



<h3>NgModel</h3>
<app-switch [(ngModel)]="value" [label]="'My Switch'"></app-switch><br />
<strong>Value:</strong> {{value}}




import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  value = false;
  
  constructor() { }
}


So now that we see what our custom form control looks like when using with Angular’s two different form API lets dig into the code for app-switch.

Building a Custom Form Control

So first let’s take a look at the template for out custom form control app-switch.



<div (click)="switch()" class="switch" [ngClass]="{ 'checked': value }" [attr.title]="label">
  <input type="checkbox" class="switch-input" [value]="value" [attr.checked]="value" [attr.aria-label]="label">
  <span class="switch-label" data-on="On" data-off="Off"></span>
  <span class="switch-handle"></span>
</div>


So in our template, we have a few dynamic properties and events. We have a click event to toggle our value. We also bind to the value to set our checkbox value and our CSS class for styles. In the post, we won’t cover the CSS file for this component, but you can dig into the source code in the working example demo at the end of this post. Next, let’s take a look at the app-switch component code and dig into the API.



import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-switch',
  templateUrl: 'app/switch.component.html',
  styleUrls: ['app/switch.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SwitchComponent),
      multi: true
    }
  ]
})
export class SwitchComponent implements ControlValueAccessor {
  @Input() label = 'switch';
  @Input('value') _value = false;
  onChange: any = () => { };
  onTouched: any = () => { };

  get value() {
    return this._value;
  }

  set value(val) {
    this._value = val;
    this.onChange(val);
    this.onTouched();
  }

  constructor() { }

  registerOnChange(fn) {
    this.onChange = fn;
  }

  registerOnTouched(fn) { 
    this.onTouched = fn;
  }

  writeValue(value) {
    if (value) {
      this.value = value;
    }
  }

  switch() {
    this.value = !this.value;
  }
}



So a lot is going on here, so let’s break it down. First our imports and @Component decorator.



import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-switch',
  templateUrl: 'app/switch.component.html',
  styleUrls: ['app/switch.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SwitchComponent),
      multi: true
    }
  ]
})


So the first part of our decorator is defining our component template, CSS, and selector. The API we are interested in is under providers. Under providers, we are telling the Angular DI to extend the existing NG_VALUE_ACCESSOR token and use SwitchComponent when requested. We then set multi to true. This mechanism enables multi providers. Essentially allowing multiple values for a single DI token. This allows natural extensions to existing APIs for devs. This essentially registers our custom component as a custom form control for Angular to process in our templates. Next, let’s look at our component class.



export class SwitchComponent implements ControlValueAccessor {
  @Input() label = 'switch';
  @Input('value') _value = false;
  onChange: any = () => { };
  onTouched: any = () => { };

  get value() {
    return this._value;
  }

  set value(val) {
    this._value = val;
    this.onChange(val);
    this.onTouched();
  }

  constructor() { }

  registerOnChange(fn) {
    this.onChange = fn;
  }

  registerOnTouched(fn) { 
    this.onTouched = fn;
  }

  writeValue(value) {
    if (value) {
      this.value = value;
    }
  }

  switch() {
    this.value = !this.value;
  }
}


So the first part of our class is the ControlValueAccessor interface we are extending. The ControlValueAccessor interface looks like this:



export interface ControlValueAccessor {
  writeValue(obj: any) : void
  registerOnChange(fn: any) : void
  registerOnTouched(fn: any) : void
}


We will go over the purpose of each one of these methods below. Our component takes in a couple of @Inputs. One is a label value so our component has the appropriate label markup and the second is for setting the component value. The @Input('input') allows us to take an input value named input and map it to the _input backing field. We will see the role of onChange and onTouched in a few. Next, we have the following getters and setters.



get value() {
  return this._value;
}

set value(val) {
  this._value = val;
  this.onChange(val);
  this.onTouched();
}


We use getters and setters to set our value on our component in a backing field named _value. This allows us to call this.onChange(val) and .onTouched().

The next method registerOnChange passes in a callback function as a parameter for us to call whenever the value has changed. We set the property onChange to the callback, so we can call it whenever our setter on the value property is called. The registerOnTouched method passes back a callback to call whenever the user has touched the custom control. When we call this callback, it notifies Angular to apply the appropriate CSS classes and validation logic to our control.



registerOnChange(fn) {
  this.onChange = fn;
}

registerOnTouched(fn) { 
  this.onTouched = fn;
}

writeValue(value) {
  if (value) {
    this.value = value;
  }
}


The last method to implement from the ControlValueAccessor is writeValue. This is called by Angular when the value of the control is set either by a parent component or form. The final method switch() is called on the click event triggered from our switch component template.

Summary

As a quick summary, custom form controls are simply components that implement the ControlValueAccessor interface. By implementing this interface, our custom controls can now work with ngModel and the Reactive Forms API. Check out the working demo below which has the corresponding CSS that creates the toggle animation effect.

Support this Blog View Code Demo