Search code examples
javascriptangularangular-materialangular-changedetection

Mat-select not updating value of second options list value


In my angular app I have a scenario where I have list of countries and if I select a country it will make an API call and fetched the respective states list. Then if I select state it should display in mat-select. I have implemented the functionality but state selection is not happening.

HTML:

<mat-form-field appearance="fill" class="full-width">
  <mat-select placeholder="Select Country/State" [(value)]="selectedValue" #countryStateSelect (openedChange)="onDropdownOpen($event)">
    <div class="dropdown-container">
      <div class="dropdown-column">
        <mat-option *ngFor="let country of countries" (click)="onCountrySelect(country, countryStateSelect)">
          {{ country.country }}
        </mat-option>
      </div>
      <div class="dropdown-column" *ngIf="selectedCountry">
        <mat-option *ngFor="let state of selectedCountry.states" [value]="state.state" (click)="onStateSelect(state.state, countryStateSelect)">
          {{ state.state }}
        </mat-option>
      </div>
    </div>
  </mat-select>
</mat-form-field>

TS:

import { Component } from '@angular/core';
import { VERSION } from '@angular/material';
import { NavItem } from './nav-item';

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss'],
})
export class AppComponent {
  selectedCountry: any = null;
  selectedValue: string = '';

  countries = [
    {
      "country": "India",
      "states": [
        { "state": "AP" },
        { "state": "TG" },
        { "state": "UP" },
        { "state": "GA" }
      ]
    },
    {
      "country": "USA",
      "states": [
        { "state": "TA" },
        { "state": "MG" },
        { "state": "NC" },
        { "state": "NY" }
      ]
    },
    {
      "country": "Canada",
      "states": [
        { "state": "ON" },
        { "state": "BC" }
      ]
    }
  ];

  onCountrySelect(country: any, select: MatSelect) {
    this.selectedCountry = country;
    select.open();
  }

  onStateSelect(state: string, select: MatSelect) {
    this.selectedValue = state;
    this.selectedCountry = null;
    select.close();
  }

  onDropdownOpen(isOpen: boolean) {
    if (!isOpen) {
      this.selectedCountry = null;
    }
  }
}

here is the example Stackblitz Demo

Can someone help me.


Solution

  • When you clear the selectedCountry the options are not available inside the select, so you are getting the selection. Instead do not set, selectedCountry to null. Also instead of value two way binding, go for ngModel instead.

    import { Component } from '@angular/core';
    import { VERSION } from '@angular/material';
    import { NavItem } from './nav-item';
    import { MatSelect } from '@angular/material/select';
    @Component({
      selector: 'material-app',
      templateUrl: 'app.component.html',
      styleUrls: ['app.component.scss'],
    })
    export class AppComponent {
      selectedCountry: any = null;
      selectedValue: string = '';
    
      countries = [
        {
          country: 'India',
          states: [
            { state: 'AP' },
            { state: 'TG' },
            { state: 'UP' },
            { state: 'GA' },
          ],
        },
        {
          country: 'USA',
          states: [
            { state: 'TA' },
            { state: 'MG' },
            { state: 'NC' },
            { state: 'NY' },
          ],
        },
        {
          country: 'Canada',
          states: [{ state: 'ON' }, { state: 'BC' }],
        },
      ];
    
      ngOnInit() {
        this.setSelectedValue('AP');
      }
    
      onCountrySelect(country: any, select: MatSelect) {
        this.selectedCountry = country;
        select.open();
      }
    
      setSelectedValue(state: any) {
        const foundCountry = this.countries.find((item: any) => {
          return item.states.some((data: any) => data.state === state);
        });
        this.selectedCountry = foundCountry;
        this.selectedValue = state;
      }
    
      onStateSelect(state: string, select: MatSelect) {
        this.selectedValue = state;
        // this.selectedCountry = null;
        select.close();
      }
    
      compareWithFn(o1: any, o2: any) {
        console.log(o1, o2);
        return o1 === o2;
      }
    
      onDropdownOpen(isOpen: boolean) {
        if (!isOpen) {
          this.selectedCountry = null;
        }
      }
    }
    

    HTML:

    <mat-form-field appearance="fill" class="full-width">
      <mat-select
        placeholder="Select Country/State"
        [(ngModel)]="selectedValue"
        #countryStateSelect
        (openedChange)="onDropdownOpen($event)"
      >
        <div class="dropdown-container">
          <div class="dropdown-column">
            <mat-option
              *ngFor="let country of countries"
              (click)="onCountrySelect(country, countryStateSelect)"
            >
              {{ country.country }}
            </mat-option>
          </div>
          <div class="dropdown-column" *ngIf="selectedCountry">
            <mat-option
              *ngFor="let state of selectedCountry.states"
              [value]="state.state"
              (click)="onStateSelect(state.state, countryStateSelect)"
            >
              {{ state.state }}
            </mat-option>
          </div>
        </div>
      </mat-select>
    </mat-form-field>
    

    Stackblitz Demo