1

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>
Suule
  • 2,197
  • 4
  • 16
  • 42
  • `custom-checkbox` is supported to use with formControlName or not ? – Developer May 09 '21 at 10:11
  • @GaurangDhorda I added code form `custom-checkbox`. And yest It supports formControlName. [controlName] translates to formControlName inside of component – Suule May 09 '21 at 10:29
  • https://stackoverflow.com/questions/31788681/angular2-validator-which-relies-on-multiple-form-fields may be this one be helpful to you – Developer May 09 '21 at 10:32

0 Answers0