Reputation: 786
Ok, I'm sorry, but I just cannot figure out how to do this... There are literally hundreds of "how do I validate my form in Angular" posts on here, but none of them quite fit what I'm trying to do, and I just cannot figure out how to make it work. So I'm asking this here. Forgive me if it's been asked before.
I'm using a reactive form in Angular 7.2.2. I'm returning a list of items from the server, and I'm dynamically creating an array of checkboxes -- one for each item. I want the user to be able to check one or more of those check-boxes -- but they must check at least one of the checkboxes, or the form should be invalid, and the submit button should be disabled.
(FYI, there are other FormControls -- either <select>
or <input type="text">
-- on my form that I have left out of this example. Those controls are not dynamically-created, so adding validators to them is trivial.)
Here's my component.html:
<form [formGroup]="myForm" (ngSubmit)="myFormSubmit()">
<div style="width: 100%; height: 160px; padding: 3px; overflow: auto;">
<label formArrayName="itemsToSelect" *ngFor="let item of myForm.controls.itemsToSelect.controls; let i = index" style="width: 85%; padding-left: 5px;">
<input type="checkbox" formControlName="{{i}}">
{{myListOfItems[i].item_id}}<br>
</label>
</div>
<input type="submit" [disable]="!this.myForm.valid">
</form>
And here's my component.ts
myListOfItems: any = [];
myForm = new FormGroup({
itemsToSelect: new FormArray([])
});
ngOnInit() {
this.myService.getMyItems()
.subscribe( (eventData) => {
myListOfItems = eventData;
myListOfItems.forEach((o, i) => {
const control = new FormControl(false); //false so the checkbox defaults to not-selected
(this.myForm.controls.itemsToSelect as FormArray).push(control);
});
});
}
myFormSubmit() {
//Do stuff here that assumes at least one checkbox is checked.
}
So... How do I get it so myForm.valid is false when all the checkboxes are blank, but true when one or more checkbox is checked? Any help would be appreciated. Thanks.
Upvotes: 2
Views: 1192
Reputation: 786
Ok, never mind. I finally figured it out...
When I create the Form Array I need to add a custom validator function:
myForm = new FormGroup({
itemsToSelect: new FormArray([], [myCustomValidator])
});
And here's what that function looks like in my component.ts file:
myCustomValidator(control: FormArray) {
let returnValue = false;
for (let i=0; i<control.value.length; i++) {
if (control.value[i]) {
returnValue = true; // If any of the controls is checked, returnValue will be set to true at some point in this loop
}
}
if (!returnValue) {
return { controlStatus: {status: 'invalid'} }; // Returning an object of some sort, doesn't matter what, is how you indicate the control is not valid.
} else {
return null; // Returning null is how you indicate the control is valid.
}
}
Upvotes: 2
Reputation: 319
You have to create separate validator for whole form. For example, you can have such validation for checkbox selection:
function checkboxAtLeastOneSelected(form: FormGroup) {
return Object.keys(form.controls)
.some(control => form.controls[control].value === true) ? null : { invalidCheckboxes: true };
}
After that, you have to place this validation function into second argument of new FormGroup:
myForm = new FormGroup({
....
}, checkboxAtLeastOneSelected);
Upvotes: 2