Hi, Are you looking to build a dependent or cascading dropdown list dropdown in Angular? Then this blog post is for you. Here you will learn how to build country-state-city dropdowns in Angular step by step. This is called dependent dropdown or cascading dropdown functionality in web development.
This post focuses on creating country, state, and city dropdowns in Angular using PHP APIs. We will also discuss how to populate the dropdowns with data from a database or API and how to populate the select options from API. You check this demo also before starting to code.
Sounds great? Let’s move forward to learn how to build the dependent dropdown in Angular material using API from scratch.
So in this example, we will use PHP to mock the REST APIs in our Angular project. We will call these API endpoints to get the list of the data for countries, states & cities.
You may also like: How to Generate PDFs in PHP: A Beginner’s Guide
Discover how to generate PDFs in PHP dynamically! Learn to generate PDFs from HTML, on button click, and save them to a folder with our comprehensive guide.
Steps for Building Dependent Dropdowns for Location in Angular
- Install Node.js & Angular CLI
- Setup Angular project & UI
- Create Components, Modules & Service files
- Mock the REST APIs
- Run the code!!
1. Install Node.js & Angular CLI
If you don’t have Node on your machine then download it from the official website and install it. Once you are done now you need to install Angular. You can install the CLI using the npm package manager:
npm install -g @angular/cli
2. Setup Angular project & UI
Now head over to your working directory in file explorer and open a command prompt or terminal and hit the following command. In this example, the project name is the “dependent_dropdown” we are creating.
ng new dependent_dropdown
This command may take some time to complete depending on the internet connection you have. Once this is completed, move to the working directory by thiscd
command and Install Angular Material via the following command.
cd dependent_dropdown npm install bootstrap ng add @angular/material
After installing these packages, go to angular.json
file, inside architect > build > styles
array add a new item "./node_modules/bootstrap/dist/css/bootstrap.min.css"
Also, if you are using Angular 15
so environment files will not be created, we need to set up those things manually. Create two files environment.ts
and environment.prod.ts
inside src > app > environments
Here is the screenshot for reference
3. Create Components, Modules & Service files
Since we are using Angular Material, hence we need to import certain modules, so instead we will create a file AngularMaterialModule
and import all required modules & finally import this module to AppModule
. We need to create a service file also, to write some business logic to call HTTP services.
ng generate module angular-material ng generate service services/dropdown // This will generate inside services folder
Update these files as follows.
AngularMaterialModule
import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; const materialModules = [ MatInputModule, MatProgressBarModule, MatProgressSpinnerModule, MatSelectModule, MatFormFieldModule, ]; @NgModule({ imports: [CommonModule, ...materialModules], exports: [...materialModules], }) export class AngularMaterialModule {}
Now import this file into the AppModule file as below.
AppModule
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AngularMaterialModule } from './angular-material.module'; import { HttpClientModule } from '@angular/common/http'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, AppRoutingModule, BrowserAnimationsModule, AngularMaterialModule, // <--added HttpClientModule, // <--added FormsModule, // <--added ReactiveFormsModule, // <--added ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
Now let’s add some HTML UI to our component HTML file. Update your app.component.html
code.
app.component.html
<mat-progress-bar mode="indeterminate" color="accent" *ngIf="loading" ></mat-progress-bar> <div class="container mt-4"> <div class="mt-3 mb-3" style="text-align: center"> <img src="https://codingbirdsonline.com/wp-content/uploads/2020/01/cropped-coding-birds-online-favicon-180x180.png" width="50" /> </div> <div class="card example-card" style="width: 50rem"> <div class="card-body"> <h5 class="card-title text-center m-2">Country, State & City Dropdown</h5> <form [formGroup]="dropdownForm"> <div class="d-flex justify-content-center mt-4"> <div class="p-2"> <mat-form-field appearance="outline"> <mat-label>Country</mat-label> <mat-select disableRipple formControlName="country" (valueChange)="selectCountry($event)" > <mat-option value="">Select</mat-option> <mat-option *ngFor="let country of countries" [value]="country.id" [attr.data-country-name]="country.name" >{{ country.name }} </mat-option> </mat-select> </mat-form-field> </div> <div class="p-2"> <mat-form-field appearance="outline"> <mat-label>State</mat-label> <mat-select disableRipple formControlName="state" (valueChange)="selectState($event)" > <mat-option value="">Select</mat-option> <mat-option *ngFor="let state of states" [value]="state.id" [attr.data-state-name]="state.name" >{{ state.name }} </mat-option> </mat-select> </mat-form-field> </div> <div class="p-2"> <mat-form-field appearance="outline"> <mat-label>City</mat-label> <mat-select disableRipple formControlName="city"> <mat-option value="">Select</mat-option> <mat-option *ngFor="let city of cities" [value]="city.id" [attr.data-city-name]="city.name" >{{ city.name }} </mat-option> </mat-select> </mat-form-field> </div> </div> <p *ngIf="dropdownForm.value.country"> {{ dropdownForm.value | json }} </p> </form> </div> </div> </div>
app.component.css
.example-card { margin: 0 auto; }
app.component.ts
import { Component, OnInit } from '@angular/core'; import { DropdownService } from './services/dropdown.service'; import { FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit { title = 'Country-State-City Dropdown'; public loading = false; public dropdownForm!: FormGroup; public countries: any[] = []; public states: any[] = []; public cities: any[] = []; constructor( private dropdownService: DropdownService, private formBuiler: FormBuilder ) {} ngOnInit(): void { /** * Intialize form with default value null */ this.dropdownForm = this.formBuiler.group({ country: [null], state: [null], city: [null], }); /** * load all country data at page load */ this.getCountries(); } /** * loads all country data * @returns void */ private getCountries() { this.loading = true; this.dropdownService.getCountries().subscribe( (response) => { this.countries = response.data; console.log(this.countries); this.loading = false; }, (error) => { console.log('Something went wrong: ', error); } ); } /** * Selects country, and gets the states for it * @param country * @returns void */ public selectCountry(country: any) { if (!country) { this.dropdownForm.controls['state'].setValue(''); this.dropdownForm.controls['city'].setValue(''); this.states = []; this.cities = []; return; } this.loading = true; const countryId = parseInt(country); this.dropdownService.getStates(countryId).subscribe( (response) => { this.states = response.data; this.loading = false; }, (error) => { console.log('Something went wrong: ', error); } ); } /** * Selects the state and gets cities for it * @param state * @returns void */ public selectState(state: any) { if (!state) { this.dropdownForm.controls['city'].setValue(''); this.cities = []; return; } this.loading = true; const stateId = parseInt(state); this.dropdownService.getCities(stateId).subscribe( (response) => { this.cities = response.data; this.loading = false; }, (error) => { console.log('Something went wrong: ', error); } ); } }
dropdown.service.ts
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root', }) export class DropdownService { // Update your API path here private BASE_URL = environment.apiUrl; constructor(private http: HttpClient) {} /** * Fetch All Countries * @returns Observable */ public getCountries(): Observable<any> { return this.http.get<any>(this.BASE_URL + 'getcountry'); } /** * Get states for a country * @param countryId * @returns Observable */ public getStates(countryId: number): Observable<any> { return this.http.get<any>( this.BASE_URL + 'getstate&country_id=' + countryId ); } /** * Get cities for a state * @param stateId * @returns Observable */ public getCities(stateId: number): Observable<any> { return this.http.get<any>(this.BASE_URL + 'getcity&state_id=' + stateId); } }
environment.ts
export const environment = { production: false, apiUrl: 'http://localhost/projects/codingbirds_/country-state-city-dropdown-api/index.php?endpoint=', // you can change your localhost path for api. };
environment.prod.ts
export const environment = { production: true, apiUrl: 'https://demo.codingbirdsonline.com/country-state-city-dropdown-api/index.php?endpoint=', };
4. Mock the REST APIs
Since our frontend work is done, now we need to set up the backend APIs, No No… You don’t need to create APIs, I have already done it for you. Download it from here & setup in locally or on your server.
Or if you do not want to set it up, you can keep the same apiUrl
into your environment.ts
file as in environment.prod.ts
I already deployed these REST APIs to my server. You can access these APIs by appending these endpoints to the base URL https://demo.codingbirdsonline.com/country-state-city-dropdown-api/
Here are endpoints, params & methods for your reference.
API endpoint | Parameters | Method | Description |
/index.php?endpoint=getcountry | .. | GET | Return the countries in json format |
/index.php?endpoint=getstate&country_id={countryId} | country_id | GET | Return the states for a country id in json format |
/index.php?endpoint=getcity&state_id={stateId} | state_id | GET | Return the cities for a state id in json format |
5. Run the code!!
So All the coding part is done, Now it is time to see the actual efforts we made to create Hierarchical location dropdowns in Angular. Save all the files and navigate to http://localhost:4200/ to see the output. You will see something like this.
Source Code & Demo
You can download the full 100% working source code from here. You check this demo also before starting to code. Watch this YouTube video explaining everything about dependent dropdown in Angular using the Country State City dropdown example.
Conclusion
I hope you learned what I explained in the above steps, If you have any suggestions or any errors, or issues please let me know in the comments, definitely I will try to help.
As I have shown If this code works for you, please leave a nice ❤️ comment so that others can find it useful which always gives me the motivation to create such content.