Cory Rylan

My name is , Google Developer Expert, Speaker, Software Developer. Building Design Systems and Web Components.

Follow @coryrylan
Angular

Creating a Dynamic Select with Angular Forms

Cory Rylan

-

Updated

This article has been updated to the latest version Angular 17 and tested with Angular 16. The content is likely still applicable for all Angular 2 + versions.

When dealing with certain forms, we need to pre-populate the form with data for the user to select or choose. Commonly this data is loaded from an asynchronous source like an API request. In this example, we will see how to set the option values in an HTML select input from an asynchronous data source. Let's get started.

Our example we will create a simple select input that will iterate over a list of orders for a user to choose like the select dropdown below.


First, we are going to create our form and select using the Reactive Forms API. To use the Reactive Forms API we need to import the ReactiveFormsModule into our application module.

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

import { AppComponent } from './app.component';

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

Next, we need to create our form. To do this, we will use the FormBuilder service which provides a convenient way to create forms.

import { Component } from '@angular/core';
import {
  FormBuilder,
  FormGroup,
  FormArray,
  FormControl,
  ValidatorFn
} from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      orders: ['']
    });
  }

  submit() {
    console.log(this.form.value);
  }
}

The form builder creates a FormGroup instance. A form group contains one to many FormControls or individual user inputs. Our single input in this form will be our orders select input.

For now, we are going to hard code our data into our component we will use to create our select dropdown.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;
  orders = [];

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      orders: ['']
    });

    this.orders = this.getOrders();
  }

  getOrders() {
    return [
      { id: '1', name: 'order 1' },
      { id: '2', name: 'order 2' },
      { id: '3', name: 'order 3' },
      { id: '4', name: 'order 4' }
    ];
  }

  submit() { ... }
}

We will loop over our orders data to build the select options in the HTML template.


<form [formGroup]="form" (ngSubmit)="submit()">
  <label for="orders">Order</label>
  <select formControlName="orders" id="orders">
    <option *ngFor="let order of orders; let i = index" [value]="orders[i].id">
      {{orders[i].name}}
    </option>
  </select>

  <button>submit</button>
</form>

For each order in our data set, we create an option for the select. For each select, we bind the order id as the option value. One thing to note whenever you bind data to a select option the value will always be converted to a string.

Now that we have built our select from data dynamically let's update our code to handle updating the select from asynchronous data. We usually get data from an API request of some kind. In our example, we will simulate an API request using an Observable to emit our data.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { of } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;
  orders = [];

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      orders: ['']
    });

    // async orders
    of(this.getOrders()).subscribe(orders => {
      this.orders = orders;
    });
  }

  getOrders() {
    return [
      { id: '1', name: 'order 1' },
      { id: '2', name: 'order 2' },
      { id: '3', name: 'order 3' },
      { id: '4', name: 'order 4' }
    ];
  }

  submit() { ... }
}

Using the RxJS of() operator we can convert our data to by async similar to if we requested the data via Angular's HTTP Service. We can subscribe to the async data and assign it to our orders property. We also need to update the select to have an initial default value once our async data has loaded. We can do this by using the patchValue method on our form control.

of(this.getOrders()).subscribe(orders => {
  this.orders = orders;
  this.form.controls.orders.patchValue(this.orders[0].id);
});

You can find the full working example in the link below!

View Demo Code   
Twitter Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

Angular

Creating Dynamic Tables in Angular

Learn how to easily create HTML tables in Angular from dynamic data sources.

Read Article
Web Components

Reusable Component Patterns - Default Slots

Learn about how to use default slots in Web Components for a more flexible API design.

Read Article
Web Components

Reusable Component Anti-Patterns - Semantic Obfuscation

Learn about UI Component API design and one of the common anti-patterns, Semantic Obfuscation.

Read Article