angularangular-materialangular-reactive-formsmat-select

Angular Material - Disable single dropdown option element based on the selection


I have an Angular Material dropdown menu, and I need to disable specific elements from that dropdown, based on which value is selected from another dropdown menu.

Here is the HTML for the two dropdowns:

<mat-form-field>
    <mat-label>Choose an option</mat-label>
    <mat-select id="lineOfBusiness" formControlName="lineOfBusiness">
        <mat-option value="commercial">Commercial</mat-option>
        <mat-option value="residential" >Residential</mat-option>
        <mat-option value="industrial">Industrial</mat-option>
    </mat-select>
</mat-form-field>


<mat-form-field>
    <mat-label>Choose an option</mat-label>
    <mat-select id="psv" formControlName="psv">
        <mat-option value="inclinometer">Inclinometer</mat-option>
        <mat-option value="heil" >Heil</mat-option>
        <mat-option value="radar">Radar</mat-option>
        <mat-option value="hydraulics">Hydraulics</mat-option>
        <mat-option value="coretex">Coretex</mat-option>
    </mat-select>
</mat-form-field>

For example, the logic would look something like this: If 'lineOfBusiness' = 'commercial', then in the 'psv' dropdown, 'radar' should be disabled.

In my form.component.ts file, I have this function, which is executing without error, but is not disabling the needed options.

disableOption(optionValue: string): void {
    console.log("Disabling " + optionValue);
    const psvControl = this.reactiveForm.get('psv');
    if (psvControl) {
        const option = psvControl.get(optionValue);
        if (option) {
            option.disable();
        }
    }
}

What am I doing wrong?


Solution

  • Key concept:

    1. Get the MatSelect DOM element for psv control via the template reference variable.

    2. Subscribe to the lineOfBusiness control's valueChanges to fire the disableOption method when the lineOfBusiness value is changed.

    3. Set the mat-option disabled when the value "commercial" is selected for the lineOfBusiness control and vice versa.

    import { Component, ViewChild } from '@angular/core';
    import { Subject, takeUntil } from 'rxjs';
    import { MatSelect } from '@angular/material/select';
    
    private destroy$ = new Subject();
    
    @ViewChild('psvSelect') psvSelect!: MatSelect;
    
    
    ngAfterViewInit() {
      this.reactiveForm.controls.lineOfBusiness.valueChanges
        .pipe(takeUntil(this.destroy$))
        .subscribe((value) => {
          this.disableOption(value);
        });
    }
    
    disableOption(optionValue: string): void {
      console.log('Disabling ' + optionValue);
    
      if (optionValue === 'commercial')
        this.psvSelect.options.find((x) => x.value == 'radar')!.disabled = true;
      else
        this.psvSelect.options.find((x) => x.value == 'radar')!.disabled = false;
    }
    
    ngOnDestroy() {
      this.destroy$.next(true);
      this.destroy$.complete();
    }
    

    You need to declare the template reference variable #psvSelect as below:

    <mat-select id="psv" formControlName="psv" #psvSelect>
        ...
    </mat-select>
    

    Demo @ StackBlitz