How to make chekbox required only if one of them is selected with FormGroup

112 Views Asked by At

I have some problem with custom validation in Angular 4 using FormGroup and FormControl. I have page with multiple checkboxes, in this example 6 of them. Three of them are required all time, and another three os required only if one or two of them was selected. If none of them is selected you can pass validation.

I am initializing my FormGroup in ngOnInit method and it looks like that

ngOnInit() {

    let statements = new FormGroup({});

    for (let i = 0; i < this.characters.length; i++) {
      const element = this.characters[i];
      statements.addControl(element.surname, new FormControl(false, CustomValidator.required));
    }

    statements.addControl(this.firstPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
       //here I have problem to write predicate which check if thirdPerson or secondPerson checbox was selected
    ));
    statements.addControl(this.secondPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
       //here I have problem to write predicate which check if firstPerson or third checbox was selected
    ));
    statements.addControl(this.thirdPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
      //here I have problem to write predicate which check if firstPerson or secondPerson checbox was selected
    ));

    this.fullData = new FormGroup({
      //here I have more than statemets, thats why I have fullData
      statements: statements
    });
  }

Here for loop is used to populate all of required checbkox. But I thought I cannot do it for thos last three, where I need to check if other two were selected. Inside of predicate I have no idea, how could I check if other two FormControls(checkboxes) was selected.

View for this components looks like that

<form [formGroup]="fullData">
  <div formGroupName="statements">
    <p>All of these 3 are required</p>
    <div *ngFor="let character of characters">
      <custom-checkbox
      [controlName]="character.surname"
      [formGroup]="fullData.get('statements')"
      [id]="character.name"
      [label]="character.name + ' ' + character.surname">
    </custom-checkbox>
    </div>

    <br />
    <br />
    <p>Only required when one of them is clicked</p>
    <custom-checkbox
      [controlName]="firstPerson.surname"
      [formGroup]="fullData.get('statements')"
      [id]="firstPerson.name"
      [label]="firstPerson.name + ' ' + firstPerson.surname">
    </custom-checkbox>

    <custom-checkbox
      [controlName]="secondPerson.surname"
      [formGroup]="fullData.get('statements')"
      [id]="secondPerson.name"
      [label]="secondPerson.name + ' ' + secondPerson.surname">
    </custom-checkbox>

    <custom-checkbox
      [controlName]="thirdPerson.surname"
      [formGroup]="fullData.get('statements')"
      [id]="thirdPerson.name"
      [label]="thirdPerson.name + ' ' + thirdPerson.surname">
    </custom-checkbox>
    <button type="button" (click)="onSubmit(fullData)"> Submit </button>
  </div>
</form>

I am using here custom checbkox, because I want use it in more places and extend it. CustomValidation class is something like this.

export interface ValidationError {
  [msg:string] : boolean;
}


export class CustomValidator {

  static required(control: any): ValidationError {
      if(control.value && (control.value.length === undefined || control.value.length >0)) {
        return null;
      }

      return {"VALIDATION.REQUIRED": false};
  }

  static requiredPredicate(predicate: any): ValidatorFn {
    return function(control: any){
      const value = predicate();
      if(value){
        return CustomValidator.required(control);
      }

      return null;
    }
  }
}

Any idea how can I do it?

CustomChecbkox code:

export class CustomCheckboxComponent implements OnInit, OnDestroy {

  @Input('label') label: string;óó
  @Input('controlName') controlName: string;
  @Input('formGroup') formGroup: FormGroup;
  @Input('id') id: string;

  @ViewChild('inputField') inputField: any;
  @ViewChild('surroundingDiv') surroundingDiv: any;

  isValid: boolean = true;
  fieldUpdated: boolean = false;
  shouldDisplayError: boolean = false;
  displayError: boolean = false;

  constructor(public cd: ChangeDetectorRef) {
  }

  ngOnDestroy(): void {
  }

  fieldChanged(value) {
    if (!this.fieldUpdated) {
      this.fieldUpdated = true;
      this.validateField(value);
    }
  }

  validateField(value): boolean {
    if (!this.fieldUpdated) {
      return true;
    }
    this.isValid = this.formGroup.get(this.controlName).errors ? false : true;
    if ((this.fieldUpdated) && !this.shouldDisplayError) {
      this.shouldDisplayError = true;
    }

    this.displayError = !this.isValid && this.shouldDisplayError;
    return !this.displayError;
  }

  ngOnInit() {
  }


}

and html

<div  class="custom-checkbox"
        [formGroup]="formGroup"
      #surroundingDiv
      [ngClass]="{'custom_validation' : displayError}">
    <label class="mdl-checkbox mdl-checkbox--pink mdl-js-checkbox mdl-js-ripple-effect"
           for="{{id}}">
        <input class="mdl-checkbox__input"
               type="checkbox"
               id="{{id}}"
               formControlName="{{controlName}}"
               (change)="fieldChanged($event.target.value)"
               #inputField
        >{{ label }}
    </label>
    <div class="custom-validation__error" *ngIf="displayError" style="padding-left: 24px;">
        <span *ngFor="let error of formGroup.get(controlName).errors "
        >{{error}}</span>
    </div>
</div>
0

There are 0 best solutions below