Angular – Form Control not working for Angular Material Mat-select

angularangular-materialform-controltypescript

I have a mat-select form element tied to a FormControl for validation. The goal is to give an error box below the element if the select box isn't filled in (or nothing is selected)

However the form is valid when submitting with no select option is chosen, even though I have a required option for it.

<form [formGroup]="cityForm" (submit)="submit()">

<mat-form-field>
   <mat-label>Select City</mat-label>

   <mat-select [(ngModel)]="city.Id"
               [formControl]="cityFormControl"
               [errorStateMatcher]="matcher">
                <mat-option *ngFor="let c of dropdowns.Cities"
                            [value]="c.Id">
                    {{c.Name}}
                </mat-option>
   </mat-select>

   <mat-error *ngIf="cityFormControl.hasError('required')">
     City is <strong>required</strong>
   </mat-error>

Controller code:

//Angular Imports
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

@Component({
    ...
})
export class CityComponent implements OnInit {

    city: any = {};
    dropdowns: any = {};

    //      Form Control Variables
    matcher = new MyErrorStateMatcher();

    cityFormControl = new FormControl('', [Validators.required]);

    cityForm = new FormGroup({
        city: this.cityFormControl
    });


    constructor() {
    }

    ngOnInit(): void {

       this.dropdowns = [
                         {Id: 1, Name: "Toronto"}, 
                         {Id: 2, Name: "Chicago"}
                        ];
    }

    submit(form : FormGroup): void {

        if (form.valid == false) {
            //doesn't get called
            return;
        }

        return;  
}

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
    isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
        const isSubmitted = form && form.submitted;
        return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
    }
}

Any idea why the required validation is not working?

Best Answer

Refer to What are the practical differences between template-driven and reactive forms?

Please have a look at the following example:

constructor(private fb: FormBuilder) { }

cityForm: FormGroup;

ngOnInit(): void {
  this.cityForm = this.fb.group({
    city: ['', [Validators.required]]
  })
}
get city() { return this.cityForm.get("city") }
<form [formGroup]="cityForm" (submit)="submit()">
  <mat-select [formControl]="city" [errorStateMatcher]="matcher">
    <mat-option *ngFor="let c of dropdowns.Cities" [value]="c.Id">
      {{c.Name}}
    </mat-option>
  </mat-select>

  <mat-error *ngIf="city.hasError('required')">
    City is <strong>required</strong>
  </mat-error>
</form>

Related Topic