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 countries
  • GET /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

Country-State dropdown demo

📌 Tips for Production

  • Use Angular Validators to 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

Popular posts from this blog

Debouncing & Throttling in RxJS: Optimizing API Calls and User Interactions

Promises in Angular

Comprehensive Guide to C# and .NET Core OOP Concepts and Language Features