Ranjith Varatharajan
Ranjith Varatharajan

Reputation: 1694

Dynamic validation based on conditions

Scenario: I have 4 form fields.

  1. Description (Optional)
  2. Select Type (Required)
  3. Phone (Required only if Select Type is set to 'Phone')
  4. Email (Required only if Select Type is set to 'Email')

When I change anything is Select Type field, based on the selection, Phone field or Email field will be visible. I need to validate these fields.

Problem:

When the form loads, it'll have only description, select type dropdown and save button.

Step 1: Click save button without entering any input, should throw an alert saying Select Type is required and select type will be red.

Step 2: Select a type, the next input becomes visible with red border. This should not happen since the user didn't touched the field. How can I solve this?

Code:

Stackblitz code

html

<div class="example-container">
    <form [formGroup]="groupForm" (ngSubmit)="onSubmit()">
        <section>
            <section class="input-row">
                <mat-form-field>
                    <input matInput type="test" placeholder="Description" id="description" formControlName="description"/>
        </mat-form-field>
            </section>
            <section class="input-row">
                <mat-form-field>
                    <mat-select id="sourceType" formControlName="sourceType" placeholder="Select Type*">
                        <mat-option value="phone">Phone</mat-option>
                        <mat-option value="email">Email</mat-option>
                    </mat-select>
                </mat-form-field>
            </section>
            <section *ngIf="typeIsPhone" class="input-row">
                <mat-form-field>
                    <input matInput type="number" placeholder="Phone" id="phoneValue" formControlName="phoneValue"/>
        </mat-form-field>
            </section>
            <section *ngIf="typeIsEmail" class="input-row">
                <mat-form-field>
                    <input matInput type="email" placeholder="Email" id="emailValue" formControlName="emailValue"/>
        </mat-form-field>
            </section>
        </section>
        <button mat-raised-button color="primary" type="submit" class="save">
      Save
    </button>
    </form>
</div> 

component:

export class FormFieldOverviewExample implements OnInit {
  typeIsPhone = false;
  typeIsEmail = false;
  public groupForm: FormGroup = new FormGroup({
    description: new FormControl(""),
    sourceType: new FormControl("", [Validators.required]),
    phoneValue: new FormControl("", [Validators.required]),
    emailValue: new FormControl("", [Validators.required])
  });

  constructor() {}

  ngOnInit(): void {
    this.groupForm
      .get("sourceType")
      .valueChanges.subscribe(this.setSourceType.bind(this));
  }

  setSourceType(SourceType: string) {
    this.typeIsPhone = SourceType === "phone";
    this.typeIsEmail = SourceType === "email";
  }

  onSubmit() {
    const sourceTypeFormControl = this.groupForm.get("sourceType");
    const phoneEnteredFormControl = this.groupForm.get("phoneValue");
    const emailEnteredFormControl = this.groupForm.get("emailValue");

    if (sourceTypeFormControl.errors.required) {
      alert("Source Type is required!");
      return;
    } else {
      if (phoneEnteredFormControl.errors.required) {
        alert("Phone is required!");
        return;
      }
      if (emailEnteredFormControl.errors.required) {
        alert("email is required!");
        return;
      }
    }
  }
}

Upvotes: 0

Views: 1558

Answers (2)

Eliseo
Eliseo

Reputation: 58099

As if a FormControl is disabled don't has errors I suggest another aproach using disable and enable that you can see in this stackblitz

ngOnInit(): void {
    const control=this.groupForm.get("sourceType")
    if (control)
       control.valueChanges.pipe( //Use startWith to execute at first
             startWith(control.value)
       ).subscribe(res=>this.setSourceType(res)); //<--see how pass the value
  }

  setSourceType(SourceType: string) {
    this.typeIsPhone = SourceType === "phone";
    this.typeIsEmail = SourceType === "email";
    const phoneControl=this.groupForm.get('phoneValue')
    const emailControl=this.groupForm.get('emailValue')
    if (phoneControl) 
      phoneControl[SourceType==='phone'?'enable':'disable']() //(*)
    if (emailControl)
      emailControl[SourceType==='email'?'enable':'disable']()
  }
  //(*) is a abreviated way to say 
  //             if (SourceType=='phone')
  //                phoneControl.enable()
  //             else
  //                phoneControl.disable()

NOTE:

  //You can not use 
  if (phoneEnteredFormControl.errors.required) //WRONG
  //use 
  if (phoneEnteredFormControl.errors && phoneEnteredFormControl.errors.required) //OK

Upvotes: 1

Akhilesh
Akhilesh

Reputation: 76

The problem stems from the submitted property, the button with implied type=submit, and the way the default ErrorStateMatcher handles this case.

Maybe use

type="button" (click)="onSubmit()"

Upvotes: 0

Related Questions