Cascading Or Dependent Dropdown Using Angular

Dependent Dropdown

In this post I will show you an example on cascading or dependent dropdown using Angular. When you have a requirement for selecting dropdown values based on another dropdown values then you need to create such cascading dropdown. For example, in your application you need to select city based on state or country then you need to create such dependent dropdown.

Here I will create some dummy dataset for country, state and city and I will show in the dropdown with select/option tags in Angular framework.

The first dropdown will contain country names, the second dropdown will show state names based on the country name and finally third dropdown will show city names based on the state name from the second dropdown.

Prerequisites

Angular 17.0.3, Node 18.17.1, Npm 10.2.3

Or

Node v12.9.1/v15.14.5, Npm 6.10.2/6.14.11, Angular 8/11

Dropdown Example

I will show you an example on cascading or dependent dropdown with select options for Country -> State -> City. So when you select Country you will have the corresponding States and when you select State you will get corresponding Cities.

Final Results

You will see the below page on the browser for the home page, where you will see three cascading dropdown as shown in the below image:

cascading or dependent dropdown using angular

Angular Application

I will create and build angular application to create cascading dropdown.

The Angular app name is angular-dependent-dropdown.

For Angular 17, you can use the following options:

cascading or dependent dropdown using angular

For Angular 11, you have the option to use stricter type checking when you create Angular app and if you use this option then you have to make sure you are using strict type for your variable.

cascading or dependent dropdown using angular

Once your new application runs successfully, you need to edit the file src/app/app.component.ts to write functions which will be called on change event so that other dropdown’s value will get auto-populated.

For Angular 17, use the following code:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, FormsModule],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
	
	constructor(private title: Title) {}

	ngOnInit() {
		this.title.setTitle('Angular Cascading or Dependent Dropdown');
	}
	
	selectedCountry: String = "--Choose Country--";
  
	Countries: Array<any> = [
		{ name: 'Germany', states: [ {name: 'A', cities: ['Duesseldorf', 'Leinfelden-Echterdingen', 'Eschborn']} ] },
		{ name: 'Spain', states: [ {name: 'B', cities: ['Barcelona']} ] },
		{ name: 'USA', states: [ {name: 'C', cities: ['Downers Grove']} ] },
		{ name: 'Mexico', states: [ {name: 'D', cities: ['Puebla']} ] },
		{ name: 'India', states: [ {name: 'E', cities: ['Delhi', 'Kolkata', 'Mumbai', 'Bangalore']} ] },
	];
  
	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;
	}
  
}

For Angular 8/11, use the following code:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
	
	constructor(private title: Title) {}

	ngOnInit() {
		this.title.setTitle('Angular Cascading or Dependent Dropdown');
	}
	
	selectedCountry: String = "--Choose Country--";
  
	Countries: Array<any> = [
		{ name: 'Germany', states: [ {name: 'A', cities: ['Duesseldorf', 'Leinfelden-Echterdingen', 'Eschborn']} ] },
		{ name: 'Spain', states: [ {name: 'B', cities: ['Barcelona']} ] },
		{ name: 'USA', states: [ {name: 'C', cities: ['Downers Grove']} ] },
		{ name: 'Mexico', states: [ {name: 'D', cities: ['Puebla']} ] },
		{ name: 'India', states: [ {name: 'E', cities: ['Delhi', 'Kolkata', 'Mumbai', 'Bangalore']} ] },
	];
  
    //states: Array<any>; //Angular 8
	states: Array<any> = []; //Angular 11

	//cities: Array<any>; //Angular 8
	cities: Array<any> = []; //Angular 11
	
	//changeCountry(country) { //Angular 8
	changeCountry(country: any) { //Angular 11
		//this.states = this.Countries.find(cntry => cntry.name == country).states; //Angular 8
		this.states = this.Countries.find((cntry: any) => cntry.name == country.target.value).states; //Angular 11
	}

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

I implement OnInit interface – a lifecycle hook that is called after Angular has initialized all data-bound properties of a directive and you need to define an ngOnInit() method to handle any additional initialization tasks – to set the title of the page otherwise you will see the default title (app’s name) of the page. Alternatively you can also set page title in the index.html file under src directory but this will set title for the whole application.

I have a class level variable selectedCountry to keep track of which country is selected.

I defined a list of countries with states and corresponding cities. These values are not completely real values, so you need to work on real values to show in the dropdown.

I have declared two variables states and cities and these variables will be populated dynamically.

I defined two methods changeCountry() and changeState() to show corresponding states and cities respectively.

You need to add FormsModule to work with the HTML form. Import and FormsModule in the src/app/app.module.ts file. The app.module.ts file is not generated or required for Angular 17.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

You can edit the file src/app/app.component.html and write below code into the file to build the cascading dropdown using select option tags.

Angular 8:

<style>
  .content {
    display: flex;
    margin: 32px auto;
    padding: 0 16px;
    max-width: 960px;
    flex-direction: column;
    align-items: center;
  }

  .card-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 16px;
  }
</style>

<div class="content">
	<h2>Cascading or Dependent Dropdown using Angular 8</h2>
	
	<div class="card-container">
		<label>Country</label>
		<select placeholder="Country" [(ngModel)]="selectedCountry" (change)="changeCountry($event.target.value)">
			<option>--Choose Country--</option>
			<option *ngFor="let country of Countries">{{country.name}}</option>
		</select>
	</div>

	<div class="card-container">
		<label>State</label>
		<select placeholder="State" (change)="changeState($event.target.value)">
			<option>--Choose State--</option>
			<option *ngFor="let state of states">{{state.name}}</option>
		</select>
	</div>
	
	<div class="card-container">
		<label>City</label>
		<select placeholder="City">
			<option>--Choose City--</option>
			<option *ngFor="let city of cities">{{city}}</option>
		</select>
	</div>
</div>

<router-outlet></router-outlet>

Angular 11/17:

<div class="content">
	<h2>Cascading or Dependent Dropdown using Angular 8/11</h2>
	
	<div class="card-container">
		<label>Country</label>
		<select placeholder="Country" [(ngModel)]="selectedCountry" (change)="changeCountry($event)">
			<option>--Choose Country--</option>
			<option *ngFor="let country of Countries">{{country.name}}</option>
		</select>
	</div>

	<div class="card-container">
		<label>State</label>
		<select placeholder="State" (change)="changeState($event)">
			<option>--Choose State--</option>
			<option *ngFor="let state of states">{{state.name}}</option>
		</select>
	</div>
	
	<div class="card-container">
		<label>City</label>
		<select placeholder="City">
			<option>--Choose City--</option>
			<option *ngFor="let city of cities">{{city}}</option>
		</select>
	</div>
</div>

<router-outlet></router-outlet>

In the above file I have done binding of selectedCountry with ngModel to track the variable’s state with values.

I am tracking dropdown’s value change using change event and populating other dropdown.

I iterate list of countries or states or cities using ngFor loop.

For Angular 11/17, I have kept the style in separate css file. The src/app/app.component.css file is updated as follows. I think you can also keep the style for Angular 8 separately in this css file and remove from the app.component.html file.

.content {
	display: flex;
	margin: 32px auto;
	padding: 0 16px;
	max-width: 960px;
	flex-direction: column;
	align-items: center;
}

.card-container {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	margin-top: 16px;
}

That’s all about how to create cascading or dependent dropdowns using Angular framework.

Source Code

Download

2 thoughts on “Cascading Or Dependent Dropdown Using Angular

  1. Great Tutorial. I did have two questions: Is there a way to not have to use [ngModel]? The dropdowns that i am creating the user has the ability to duplicate and when they do the vaules in the select option are the same.

  2. In app.component.ts file, I was getting an error below.
    changeState(state) {
    this.cities = this.Countries.find(cntry => cntry.name == this.selectedCountry).states.find(stat => stat.name == state).cities;
    }
    So I made following changes and its working for me, please look into it.
    changeState(state) {
    this.cities = this.states.find(stat => stat.name == state).cities;

    }

Leave a Reply

Your email address will not be published. Required fields are marked *