Search code examples
angulartypescriptvalidationmaterial-uiangular-material

Angular Material UI - How to add onBlur validation for checkboxes/radio buttons?


I'm trying to create some simple checkboxes and radio buttons with Angular Material UI. The problem I'm having is getting the red outline onBlur effect when the field is required and the user doesn't select an option. I added the requiredTrue validator, but it doesn't work. Sample HTML below.

    <div>
        @for (i of checkboxValues; track i){
            <mat-checkbox 
                [value]="i.key"
                [checked]="i.selected"             
            >
                {{i.value}}
            </mat-checkbox>
        }
    </div>
<mat-radio-group [formControl]="radioFormControl">
    @for (i of radioValues; track i){        
        <mat-radio-button 
            [value]="opt" 
        >
            {{i.value}}
        </mat-radio-button>
    }
</mat-radio-group>

I tried wrapping the <mat-option> and <mat-checkboxes> in a <mat-form-field> but I end up getting an error related to 'Error: mat-form-field must contain a MatFormFieldControl' which I can't seem to resolve. I'm new to angular so maybe there's something I'm missing.


Solution

  • The answer to your question is to use custom CSS to create the red border, the CSS will look like below.

    .mat-mdc-radio-group.ng-touched.ng-invalid {
      .mdc-radio__outer-circle {
        border-color: red !important;
      }
      .mdc-label {
        color: red;
      }
    }
    
    .mat-mdc-checkbox.ng-touched.ng-invalid {
      .mdc-checkbox__background {
        border-color: red !important;
      }
      .mdc-label {
        color: red !important;
      }
    }
    

    The main problem in your approach is with the checkboxes, red validation cannot be placed, unless you want the user to mandatory check the check-box. Other than that, it's possible to be done for radio group easily, without the mat form field.

    Full Code:

    HTML:

    <form [formGroup]="form" (ngSubmit)="form.markAllAsTouched();">
      <mat-label>Enter your Selection</mat-label>
      <br />
      <mat-radio-group aria-label="Select an option" formControlName="radios">
        @for (item of radioValues; track item){
        <mat-radio-button [value]="item.id">{{item.label}}</mat-radio-button>
        }
      </mat-radio-group>
      <br />
      @if(form.get('radios'); as radio) { @if (radio?.touched &&
      radio?.errors?.required) {
      <mat-error>Selection is required</mat-error>
      } }
      <hr />
      <mat-label>Enter your Checkboxes</mat-label>
      @for (item of checkboxValues; track item){
      <mat-checkbox [value]="item.key" [formControlName]="item.value">
        {{item.value}}
      </mat-checkbox>
      }
      <hr />
      <br />
      <button type="submit">submit</button>
    </form>
    

    TS:

    import { Component } from '@angular/core';
    import {
      FormControl,
      FormGroup,
      ReactiveFormsModule,
      Validators,
    } from '@angular/forms';
    import { MatRadioModule } from '@angular/material/radio';
    import { MatCheckboxModule } from '@angular/material/checkbox';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { CommonModule } from '@angular/common';
    
    /**
     * @title Basic radios
     */
    @Component({
      selector: 'radio-overview-example',
      templateUrl: 'radio-overview-example.html',
      styleUrl: 'radio-overview-example.css',
      standalone: true,
      imports: [
        MatRadioModule,
        ReactiveFormsModule,
        MatCheckboxModule,
        CommonModule,
        MatFormFieldModule,
      ],
    })
    export class RadioOverviewExample {
      radioValues = [
        { id: 1, label: 'apple' },
        { id: 2, label: 'banana' },
      ];
      checkboxValues = [
        { key: 'apple', value: 'apple', selected: false },
        { key: 'banana', value: 'banana', selected: false },
        { key: 'orange', value: 'orange', selected: false },
      ];
      form = new FormGroup({
        radios: new FormControl(null, Validators.required),
      });
    
      ngOnInit() {
        this.checkboxValues.forEach((item: any) => {
          this.form.addControl(
            item.value,
            new FormControl(null, Validators.required)
          );
        });
        // this.form.get('radios')!.markAsTouched();
        // this.form.get('radios')!.markAsDirty();
      }
    }
    

    Click on submit to see the errors.

    Stackblitz Demo