Reusable Angular: Create multiple checkboxes component with Forms

Vladislav Guleaev
2 min readJul 12, 2018

--

At previous post I created checkbox group component using template data binding. This time I will do the same job using Angular Reactive Forms.

Angular forms is used to build form controls dynamically and provide a lot of flexibility to configure html elements.

Let’s try to rebuild CheckboxGroup component. First of all we need to change template. We add a <form> tag and bind form property. Also we will use a FormArray to put some FormControls there.

<form [formGroup]=”form”>
<div formArrayName=”items”>
<div *ngFor=”let item of items.controls; index as i”>
<input type=”checkbox” [formControlName]=”i”> {{options[i].label}}
</div>
</div>
</form>

In typescript file we create a property for FormGroup with nested FormArray called items.

@Component({
selector: ‘checkbox-group’,
templateUrl: ‘./checkbox-group.component.html’,
styleUrls: [‘./checkbox-group.component.css’]
})
export class CheckboxGroupComponent implements OnInit, OnChanges {
@Input() options = Array<CheckboxItem>();
@Input() selectedValues = Array<string>();
@Output() toggle = new EventEmitter<any[]>();
form = new FormGroup({
items: new FormArray([])
});
constructor() { }get items(): FormArray {
return this.form.get(‘items’) as FormArray;
}
}

It’s nice to create a getter for items property of a FormGroup to use strong typing.

Next step is to implement the ngOnInit and ngOnChanges functions.

ngOnInit() {
this.form.valueChanges.subscribe(value => {
const optionsChecked = new Array<any>();
for (let index = 0; index < this.items.length; index++) {
const isOptionChecked = this.items.get(index.toString()).value;
if (isOptionChecked) {
const currentOptionValue = this.options[index].value;
optionsChecked.push(currentOptionValue);
}
}
this.toggle.emit(optionsChecked);
});
}

When any FormControl of a ForGroup changes the event valueChanges is fired. We need to subscribe to this event to return all selected values of our checkbox group every time user click on any checkbox.

The for loop simply finds the same index in FormArray as in available options. This is need because our FormControl doesn't have an “Id” property so we need to do some mapping by array index (order).

ngOnChanges() {
if (this.items.length === 0) {
this.options.forEach(x => {
this.items.push(new FormControl(false));
});
}
this.selectedValues.forEach(value => {
const index: number = this.options.findIndex(opt => opt.value === value);
if (index >= 0) {
this.items.get(index.toString()).setValue(true);
}
});
}

We use ngOnChanges not ngOnInit because if data from backend will be fetched with result after this component rendered, no checkboxes will be pushed in items array.

This approach seems to be better because checked state of every checkbox is stored into FormGroup variables not in options array. Therefore our checkbox component cannot change options array passed from parent component. Less error prone!

Usage:

<checkbox-group [options]=”userRolesOptions” [selectedValues]=”userModel.roles” (toggle)=”onRolesChange($event)”></checkbox-group>
Angular Material styles

After this you should get exactly the same behavior as in previous article.

Hope you enjoy using Angular Forms! Thanks for reading :)

--

--

Vladislav Guleaev

Fullstack Javascript Developer. Lives in Munich. Born in Moldova Republic.