Search code examples
javascriptformsangularvalidationangular4-forms

Form Validation between multiple input fields


So I have a form with multiple input form fields,and a nested form group. In the nested formgroup, the validation should be such that if any one of the three inputs is filled, then the form should be valid. The person can fill either all, or even just one and the form should be valid. My template code is as follows:

 <h3 class="form-title">{{title}}</h3>
<form [formGroup]="formX" novalidate>
    <div formGroupName="brandIdentifier">
        <mat-form-field class="full-width">
            <mat-icon matSuffix color="primary" *ngIf="brandName.valid && brandName.touched">done</mat-icon>
            <mat-icon matSuffix color="primary" *ngIf="brandName.invalid && brandName.touched">close</mat-icon>
            <input matInput type="text" placeholder="Brand Name" formControlName="brandName" />
        </mat-form-field>
        <mat-form-field class="full-width">
            <mat-icon matSuffix color="primary" *ngIf="brandId.valid && brandId.touched">done</mat-icon>
            <mat-icon matSuffix color="primary" *ngIf="brandId.invalid && brandId.touched">close</mat-icon>
            <input matInput type="text" placeholder="Brand Id" formControlName="brandId" />
        </mat-form-field>
        <mat-form-field class="full-width">
            <mat-icon matSuffix color="primary" *ngIf="contentId.valid && contentId.touched">done</mat-icon>
            <mat-icon matSuffix color="primary" *ngIf="contentId.invalid && contentId.touched">close</mat-icon>
            <input matInput type="text" placeholder="Content Id" formControlName="contentId" />
        </mat-form-field>
    </div>
    <mat-form-field class="full-width">
        <mat-icon matSuffix color="primary" *ngIf="startTime.valid && startTime.touched">done</mat-icon>
        <mat-icon matSuffix color="primary" *ngIf="startTime.invalid && startTime.touched">close</mat-icon>
        <input matInput type="text" placeholder="Start Time" formControlName="startTime" />
    </mat-form-field>
    <mat-form-field class="full-width">
        <mat-icon matSuffix color="primary" *ngIf="endTime.valid && endTime.touched">done</mat-icon>
        <mat-icon matSuffix color="primary" *ngIf="endTime.invalid && endTime.touched">close</mat-icon>
        <input matInput type="text" placeholder="End Time" formControlName="endTime" />
    </mat-form-field>

    <button mat-raised-button color="primary" (click)="startAnalysis()" [ngClass]="{'form-valid':formX.valid, 'form-invalid':formX.invalid}">Submit</button>

    <pre>{{formX.value | json}}</pre>
</form>

How would I go about this? using Validator class is a given, but I'm unable to get it optional.


Solution

  • You can use a custom validator for this, apply the custom validator for the nested group, where we check that at least one of the three fields has a value else than empty string, so here's a sample:

    Build form:

    this.myForm = this.fb.group({
      nestedGroup: this.fb.group({
        first: [''],
        second: [''],
        third: ['']
        // apply custom validator for the nested group only
      }, {validator: this.myCustomValidator})
    });
    

    Custom validator:

     myCustomValidator(group: FormGroup) {
       // if true, all fields are empty
       let bool = Object.keys(group.value).every(x => group.value[x] === '')
       if(bool) {
         // return validation error
         return { allEmpty: true}
       }
       // valid
       return null;
     }
    

    Then in your form you can show the validation message by:

    <small *ngIf="myForm.hasError('allEmpty','nestedGroup')">
      Please fill at least one field
    </small>
    

    Finally a DEMO :)