Angular cascading mat-select dropdowns

3.4k views Asked by At

I am trying to make a mat-select cascading dropdown list. The problem is that when I click on the "Country" dropdown and select an option, it does not appear as selected so I can't even test the whole cascading dropdown.

dependent-dropdown-component.html:

<div class="content">
<mat-form-field appearance="outline">
    <mat-label>Country</mat-label>
    <mat-select [(ngModel)]="selectedCountry" (change)="changeCountry($event)">
        <mat-option *ngFor="let country of Countries">{{country.name}}</mat-option>
    </mat-select>
</mat-form-field>

<mat-form-field appearance="outline">
    <mat-label>State</mat-label>
    <mat-select (change)="changeState($event)">
        <mat-option *ngFor="let state of states">{{state.name}}</mat-option>
    </mat-select>
</mat-form-field>

<mat-form-field appearance="outline">
    <mat-label>City</mat-label>
    <mat-select>
        <mat-option *ngFor="let city of cities">{{city}}</mat-option>
    </mat-select>
</mat-form-field>

dependent-dropdown-component.ts:

import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';

@Component({
  selector: 'app-dependent-dropdown',
  templateUrl: './dependent-dropdown.component.html',
  styleUrls: ['./dependent-dropdown.component.css']
})

export class DependentDropdownComponent implements OnInit {
  constructor(private title: Title) { }

  ngOnInit() { }

  selectedCountry: String = "ChooseCountry";

  Countries: Array<any> = [
    {
      name: 'Romania',
      states: [
        {
          name: 'Cluj',
          cities: ['Cluj-Napoca', 'Dej', 'Gherla']
        },
        {
          name: 'Sibiu',
          cities: ['Sibiu', 'Avrig', 'Cisnadie']
        },
        {
          name: 'Satu Mare',
          cities: ['Baia-Mare', 'Satu-Mare', 'Carei']
        },
      ]
    },

    {
      name: 'United States',
      states: [
        {
          name: 'Washington',
          cities: ['Seattle', 'Vancouver', 'Kent']
        },
        {
          name: 'Texas',
          cities: ['Houston', 'El Paso', 'Dallas']
        },
        {
          name: 'California',
          cities: ['Los Angeles', 'San Francisco', 'San Diego']
        },
      ]
    },
  ];

  states: Array<any> = [];
  cities: Array<any> = [];

  changeCountry(country: any) {
    this.states = this.Countries.find((cntry: any) => cntry.name == country.target.value).states;
  }

  changeState(state: any) {
    this.cities = this.Countries.find((cntry: any) => cntry.name == this.selectedCountry).states.find((stat: any) => stat.name == state.target.value).cities;
  }
}

source: https://roytuts.com/cascading-or-dependent-dropdown-using-angular/

1

There are 1 answers

2
Octavian Mărculescu On BEST ANSWER

The first issue I notice here is that you don't have a value attribute on the mat-options. This is why the selected country does not stay selected. To solve that, add the [value]="country" binding on the mat-option (same goes for the other dropdowns as well). Secondly, there does not seem to be any change event emitted by the mat-select. Looking at the docs I supposed you are looking for the valueChange event instead.

<div class="content">
  <mat-form-field appearance="outline">
    <mat-label>Country</mat-label>
    <mat-select [(ngModel)]="selectedCountry" (valueChange)="changeCountry($event)">
      <mat-option *ngFor="let country of Countries" [value]="country">{{
        country.name
      }}</mat-option>
    </mat-select>
  </mat-form-field>

  <mat-form-field appearance="outline">
    <mat-label>State</mat-label>
    <mat-select (valueChange)="changeState($event)">
      <mat-option *ngFor="let state of states" [value]="state">{{ state.name }}</mat-option>
    </mat-select>
  </mat-form-field>

  <mat-form-field appearance="outline">
    <mat-label>City</mat-label>
    <mat-select>
      <mat-option *ngFor="let city of cities" [value]="city">{{ city }}</mat-option>
    </mat-select>
  </mat-form-field>
</div>

The event handlers can also be a bit simpler:

changeCountry(country: any) {
  this.states = country.states;
  this.cities = [];
}

changeState(state: any) {
  this.cities = state.cities;
}