Keywords: Angular | Reactive Forms | Checkbox Array | FormArray | Value Binding
Abstract: This article explores methods to generate an array of selected values instead of simple booleans when multiple checkboxes are bound to the same formControlName in Angular Reactive Forms. By leveraging FormArray and change event handling, it demonstrates how to transform checkbox states into value arrays, with complete code examples and implementation steps.
Problem Background and Requirements Analysis
In Angular application development, form handling is a common requirement. When using Reactive Forms, developers may encounter a scenario where multiple checkboxes bound to the same formControlName return boolean values (true or false) by default, rather than the expected array of selected values. For example, with three checkboxes corresponding to values "value-1", "value-2", and "value-3", if the user selects the first two, the form control value should be ["value-1", "value-2"], not simple boolean states.
Core Solution: Utilizing FormArray
Angular's Reactive Forms provide the FormArray class, designed for managing dynamic collections of form controls. By encapsulating the checkbox group within a FormArray, developers can flexibly handle each checkbox's state and dynamically update the value array based on user interactions.
Component Logic Implementation
First, define the checkbox data model and form structure in the component. Assume a ChoiceClass with description and value properties:
public checks: Array<ChoiceClass> = [
{description: 'Description 1', value: 'value1'},
{description: "Description 2", value: 'value2'},
{description: "Description 3", value: 'value3'}
];
initModelForm(): FormGroup{
return this._fb.group({
otherControls: [''],
myChoices: new FormArray([]),
});
}
Here, myChoices is initialized as an empty FormArray to dynamically add checkbox controls later.
Template Binding and Event Handling
In the template, use *ngFor to iterate over the checks array and bind the change event for each checkbox:
<div *ngFor="let choice of checks; let i=index" class="col-md-2">
<label>
<input type="checkbox" [value]="choice.value" (change)="onCheckChange($event)">
{{choice.description}}
</label>
</div>
The key is the onCheckChange method, which handles the logic for checkbox selection and deselection:
onCheckChange(event) {
const formArray: FormArray = this.myForm.get('myChoices') as FormArray;
if(event.target.checked){
formArray.push(new FormControl(event.target.value));
} else {
let i: number = 0;
formArray.controls.forEach((ctrl: FormControl) => {
if(ctrl.value == event.target.value) {
formArray.removeAt(i);
return;
}
i++;
});
}
}
When a checkbox is checked, a new FormControl with the checkbox's value is added to the FormArray; when unchecked, the control with the corresponding value is removed. This ensures that myChoices always contains an array of selected values.
Data Submission and Model Representation
Upon form submission, the value of myChoices is directly the desired array, for example:
{
otherControls: "foo",
myChoices: ['value1', 'value2']
}
This approach avoids additional processing to convert boolean values to actual values during submission, simplifying the data flow.
Supplementary and Optimization Considerations
Referencing other answers, further optimizations can be made:
- Initial Value Handling: If the model has pre-selected values, populate the
FormArrayduring initialization by iterating over thechecksarray and addingFormControlinstances based on initial states. - Using FormBuilder: As shown in Answer 2,
FormBuildercan simplifyFormArraycreation, e.g.,_fb.array([true, false, true]), but note this generates a boolean array and requires event handling to convert to value arrays. - Angular 6+ keyvalue Pipe: Answer 3 mentions that in Angular 6+, the
keyvaluepipe can be used withFormGroupto achieve similar functionality, avoiding index-based approaches and improving readability. - Value Changes Monitoring and Filtering: Answer 4 suggests using the
valueChangesobservable to monitor changes and filter outfalsevalues upon submission. Care must be taken to avoid infinite loops by using theemitEvent: falseoption.
Practical Application Scenarios
Combining insights from the reference article, this method is suitable for dynamically generated checkbox lists, such as order selections or permission configurations. For instance, when fetching option data asynchronously from an API, call the addCheckboxes method after data loads to dynamically add controls. Additionally, validation logic can be integrated, e.g., ensuring at least one checkbox is selected via custom validators.
Conclusion
By combining FormArray with event handling, Angular Reactive Forms efficiently manage checkbox groups and output value arrays. This method not only meets the initial requirements but also offers extensibility for integrating advanced features like validation and asynchronous data binding. Developers should choose implementation approaches based on specific scenarios, balancing code simplicity and functional completeness.