Form Validation between multiple input fields

Viewed 661

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.

2 Answers

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 :)

Please follow below steps:

Note: we are just giving idea how you can solve your problem.

  1. import FormsModule, ReactiveFormsModule in module.ts

    import { FormsModule, ReactiveFormsModule } from '@angular/forms';

    @NgModule({ imports: [FormsModule, ReactiveFormsModule]})

  2. Modify your html as given e.g.

    User name User name is required. User name must be at least 4 characters long. Password Password is required. Password must be at least 6 characters long. Forget the password ?

                                    <div class="form-group">
                                        <button type="submit" (click)='onSubmit()' dir-btn-click  [disabled]="!loginForm.valid" class="btn btn-inverse btn-block">Sign in</button>
                                    </div>
    
                                </form>
    
  3. Modify code as below in [yourcomponent].ts e.g.:

    import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';

    @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent {

    loginForm: FormGroup;

    constructor( private fb: FormBuilder) { this.crateLoginForm() }

    get userName() { return this.loginForm.get('userName'); } get password() { return this.loginForm.get('password'); }

    crateLoginForm() { this.loginForm = this.fb.group({ userName: ['', [Validators.required, Validators.minLength(4)]], password: ['', [Validators.required, Validators.minLength(6)]] }) }

    onSubmit() { let userName= this.loginForm.value.userName; let pwd =this.loginForm.value.password; }

    }

Related