Angular multiple check boxes

Andrei Mihalciuc
3 min readOct 18, 2017

Recently whilst working on a project, I came across a problem — how to implement the use of multiple check boxes in a form?

The requirements were the following:

  • should support reactive and template forms
  • selection can be controlled by code - select / deselect all, reset form
  • result should be an array of selected values

If you want to skip the explanation, you can play with the code straight away in stackblitz.

Idea - we should create a group component which holds ngModel or formControlName and then children components (checkboxes), which update the group component’s value. Here is how the final usage will look:

<checkbox-group [(ngModel)]="selectedItems">
<checkbox value="item1">Item 1</checkbox>
<checkbox value="item2">Item 2</checkbox>
<checkbox value="item3">Item 3</checkbox>
<checkbox value="item4">Item 4</checkbox>
</checkbox-group>

To be able to work with angular forms the first thing that comes in mind is the ControlValueAccessor interface. By implementing this interface our component seamlessly integrates with angular forms. Let’s start with checkbox-group component:

@Component({
selector: 'checkbox-group',
template: `<ng-content></ng-content>`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxGroupComponent),
multi: true
}
]
})
export class CheckboxGroupComponent implements ControlValueAccessor {
private _model: any;
private onChange: (m: any) => void;
private onTouched: (m: any) => void;
get model() {
return this._model;
}
writeValue(value: any): void {
this._model = value;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}

set(value: any) {
this._model = value;
this.onChange(this._model);
}
}

The writeValue, registerOnChange and registerOnTouched methods are implementation of the ControlValueAccessor interface (official documentation describes them very well, so I won’t go into details). _model is used to store internal value. set() is a helper method for setting the initial value.

To register our value accessor with Angular we extend NG_VALUE_ACCESSOR with our component:

providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxGroupComponent),
multi: true
}]

Our default implementation is ready, let’s implement the code which will work with arrays. Add the following code after the set() method:

addOrRemove(value: any) {
if (this.contains(value)) {
this.remove(value);
} else {
this.add(value);
}
}

Method addOrRemove is called when our child checkbox changes its selection. If the checkbox’s value is already in model then remove it, otherwise add it to our _model. The contains() methods just verifies if value is in the model:

contains(value: any): boolean {
if (this._model instanceof Array) {
return this._model.indexOf(value) > -1;
} else if (!!this._model) {
return this._model === value;
}
return false;
}

For completeness here is the implementation for add and remove methods:

private add(value: any) {
if (!this.contains(value)) {
if (this._model instanceof Array) {
this._model.push(value);
} else {
this._model = [value];
}
this.onChange(this._model);
}
}
private remove(value: any) {
const index = this._model.indexOf(value);
if (!this._model || index < 0) {
return;
}
this._model.splice(index, 1);
this.onChange(this._model);
}

When the _model value is changed, we call this.onChange to notify Angular about our changes.

The group component is ready. Let’s start on the checkbox component:

import { Component, Input, Host } from '@angular/core';
import { CheckboxGroupComponent } from './checkbox-group.component';
@Component({
selector: 'checkbox',
template: `
<div (click)="toggleCheck()">
<input type="checkbox" [checked]="isChecked()" />
<ng-content></ng-content>
</div>`
})
export class CheckboxComponent {
@Input() value: any;
constructor(@Host() private checkboxGroup: CheckboxGroupComponent) {} toggleCheck() {
this.checkboxGroup.addOrRemove(this.value);
}
isChecked() {
return this.checkboxGroup.contains(this.value);
}
}

@Host decorator allows us to get a reference of our parent CheckboxGroupElement. When the checkbox’s selection is changed the toggleCheck() method is called, which adds or removes the value to/from the group component. isChecked() method is then bound to [checked] property, so if the component’s value is in the parent’s model, then the checkbox is checked, otherwise it is unchecked.

Summary

As you’ve just seen it is really easy to implement a control with multiple checkboxes.

Below is a stackblitx example where you can play with the code and see usages.

This pattern can be used in all kinds of controls where you need an array of values. For example combo boxes and multiple value dropdowns. One great way I have utilised this technique was for the creation of a country selection component.

If any of this article is unclear please post below and I’ll be happy to help you.

--

--