Dynamic Country and State Dropdowns in Angular using Web API
Modern Angular applications often need user input forms where country and state selections should be dynamic. This guide demonstrates how to fetch and bind them from a Web API using reactive Angular forms.
🌐 Backend API Endpoints
GET /api/countries– List of all countriesGET /api/states?countryId=1– States based on selected country
📄 Sample API Response
// Country
{ "id": 1, "name": "United States" }
// State
{ "id": 101, "name": "California", "countryId": 1 }
🔧 Step 1: Angular Setup
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [HttpClientModule]
})
export class AppModule {}
🧩 Step 2: Define Interfaces
export interface Country {
id: number;
name: string;
}
export interface State {
id: number;
name: string;
countryId: number;
}
📦 Step 3: Country Service
@Injectable({ providedIn: 'root' })
export class CountryService {
private apiUrl = 'https://yourapi.com/api';
constructor(private http: HttpClient) {}
getCountries(): Observable {
return this.http.get(`${this.apiUrl}/countries`).pipe(
catchError(error => {
console.error('Error loading countries', error);
return of([]);
})
);
}
getStatesByCountryId(countryId: number): Observable {
return this.http.get(`${this.apiUrl}/states?countryId=${countryId}`).pipe(
catchError(error => {
console.error('Error loading states', error);
return of([]);
})
);
}
}
👨💼 Step 4: Employee Form Component
@Component({
selector: 'app-employee-form',
templateUrl: './employee-form.component.html'
})
export class EmployeeFormComponent implements OnInit {
form: FormGroup;
countries: Country[] = [];
states: State[] = [];
loadingCountries = true;
loadingStates = false;
constructor(
private fb: FormBuilder,
private countryService: CountryService
) {
this.form = this.fb.group({
name: [''],
country: [''],
state: ['']
});
}
ngOnInit() {
this.countryService.getCountries().subscribe(data => {
this.countries = data;
this.loadingCountries = false;
});
this.form.get('country')?.valueChanges.subscribe(countryId => {
if (countryId) {
this.loadingStates = true;
this.countryService.getStatesByCountryId(countryId).subscribe(states => {
this.states = states;
this.loadingStates = false;
this.form.get('state')?.setValue('');
});
} else {
this.states = [];
this.form.get('state')?.setValue('');
}
});
}
onSubmit() {
console.log('Form submitted:', this.form.value);
}
}
📝 Step 5: Template (employee-form.component.html)
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label>Name:</label>
<input formControlName="name" type="text" class="form-control" />
<label>Country:</label>
<select formControlName="country" class="form-select">
<option value="">Select Country</option>
<option *ngFor="let country of countries" [value]="country.id">
{{ country.name }}
</option>
</select>
<span *ngIf="loadingCountries">Loading countries...</span>
<label>State:</label>
<select formControlName="state" [disabled]="!states.length" class="form-select">
<option value="">Select State</option>
<option *ngFor="let state of states" [value]="state.id">
{{ state.name }}
</option>
</select>
<span *ngIf="loadingStates">Loading states...</span>
<button type="submit" class="btn btn-primary mt-3">Submit</button>
</form>
📽️ Visual Flow
📌 Tips for Production
- Use Angular
Validatorsto ensure clean form submissions. - Improve UX with spinners or loaders.
- Use caching to avoid repetitive backend calls for static data.
- Provide fallback messages for failed HTTP calls.
🔚 Conclusion
You’ve now created a fully dynamic Angular form using backend-driven dropdowns. This method ensures scalability and is a real-world pattern for enterprise apps like CRMs and HR systems.
Comments
Post a Comment