cherrypop
cherrypop

Reputation: 41

How to validate at least one checkbox is selected in Angular

I have a list of angular checkboxes and need to validate if at least one is selected.

The checkbox items are dynamic(they come from a service) so it is not a static list. One time it could have 3 elements and other time could have 50+ etc.

The template goes like this:

<tr *ngFor="let recipient of recipients">
      <td class="disable edit">
        <div>
          <span class = "textbluesmall"><input type="checkbox"   [checked]="true" value="{{recipient?.participantId }}" name="participant" />{{recipient?.lastName }} ,{{recipient?.firstName }}</span>
        </div>
      </td>
      <td class="disable edit">
        <div>
          <span>{{recipient?.email }}</span>
        </div>
      </td>
    </tr>

And I'm trying to solve this way but it is not working

.TS FILE:

  var checkboxs=document.getElementsByName('participant');
  var okay=false;
  for(var i=0,l=checkboxs.length;i<l;i++)
  {
    if(checkboxs[i].checked){
      okay=true;
      break;
    }
  }
  if(okay){
    return true;
  }
  else{
    return false;
  }
} 

How can I do this the easiest way?

Upvotes: 3

Views: 7770

Answers (4)

spiritworld
spiritworld

Reputation: 143

Less work: set required attribute dynamically true when nothing is selected, this will invalidate your form.

// ANGULAR
check(recipients): boolean {
    return !recipients.some(el => el.checked);
}

// HTML
<ng-form #recipientForm="ngForm" name="form">
    <input type="checkbox" 
           [required]="check(recipients)" />

Upvotes: 0

NiB
NiB

Reputation: 959

your constructor

constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      recipients: new FormArray([], minSelectedCheckboxes(1))
    });

add this function

function minSelectedCheckboxes(min = 1) {
  const validator: ValidatorFn = (formArray: AbstractControl) => {
    if (formArray instanceof FormArray) {
      const totalSelected = formArray.controls
        .map((control) => control.value)
        .reduce((prev, next) => (next ? prev + next : prev), 0);
      return totalSelected >= min ? null : { required: true };
    }

    throw new Error('formArray is not an instance of FormArray');
  };

  return validator;
}

Upvotes: 6

cherrypop
cherrypop

Reputation: 41

The correct way to do this is with Reactive Forms as @thisdotutkarsh answered. But actually if you need to do it the "Raw way" you can do it like this:

validateCheckboxes() {
this.vals = document.getElementsByName('participant');
var recpNum = 0;

for (var i = 0, n = this.vals.length; i < n; i++) {
  if  (this.vals[i].checked ){
    recpNum++;
  }
}
if(recpNum == 0){
  return false;
}else{
  if(recpNum > 0){
  return  true
  }
}}

Upvotes: 0

thisdotutkarsh
thisdotutkarsh

Reputation: 980

As the checkboxes have to be created dynamically, you need to use Reactive Forms in Angular.

Reactive Forms are primarily composed of three types of classes, namely,

  • FormGroups
  • FormArray
  • FormControl

To be able to create checkboxes, dynamically you would need to start by importing FormsModule and ReactiveFormsModule inside your module.ts file, and add them to the imports array of your @NgModule decorator.

Similarly import FormBuilder and FormGroup in your component.ts file. Then use the FormBuilder service to create your FormGroup that will be declared in your template.

export class AppComponent {
  form: FormGroup;
  arrayOfRecepients = [];

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      recipients: new FormArray([])
    });
  }

}

In your template file, bind the form element with [formGroup]="form"

<form [formGroup]="form">
  <!-- Template code for checkboxes -->
</form>

Iterate through your array of recipients to push new instances of FormControl to FormArray. Set the control to false, so that the checkboxes are marked unchecked. This way the form array will consider each individual checkbox as a part of its collection of controls.

this.arrayOfRecepients.forEach(() => this.recipientsFormArray.push(new FormControl(false)));

In your template file, iterate over your array of recipients using the *ngFor directive. Define the formArrayName as recipients as it is required for informing the form API.

<form [formGroup]="form" (ngSubmit)="submit()">
  <label formArrayName="recipients" *ngFor="let recepient of recipientFormArray.controls; let i = index">
    <input type="checkbox" [formControlName]="i">
    {{arrayOfRecipients[i].name}}
  </label>
</form>

For validating the checkboxes, you would need to pass in your validator function as a second parameter to the FormArray.

The validator function should be such that it takes your formArray and count of least checked checkboxes required, as input. It should grab the values of the control, calculate the count of control values that are true, and then compare the count with the minimum.

TLDR;

For your reference please find link to a working demo here

Upvotes: 1

Related Questions