Angular Select Option using Reactive Form
April 29, 2019
This page will walk through Angular select option + multiple select option + validation example using Reactive Form. We will provide end to end demo for how to populate select option and multiple select options. In Angular select element, the value
attribute of <option>
element can bind an object using property binding. For validation of select element, we need to use validation attribute such as required
as select element attribute or Validators.required
in FormControl
. Select element is written using <select>
tag and multiple select element uses multiple
attribute as <select multiple>
. By default select element compares two objects using object identity. If we want to change the default behavior, we need to use compareWith
input in select element. Now find the complete example step-by-step.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 7.0.0
2. Angular CLI 7.0.3
3. TypeScript 3.1.1
4. Node.js 10.3.0
5. NPM 6.1.0
Create a FormGroup
We will create aFormGroup
using FormBuilder
.
userForm: FormGroup; this.userForm = this.formBuilder.group({ profile: [null, [ Validators.required ] ], userName: ['', [ Validators.required ] ], technologies: [null, [ Validators.required ] ] });
profile
form control will be used for single select dropdown and technologies
form control will be used for multiple select options in our demo.
Single Select Option
In Angular,SelectControlValueAccessor
writes values and listens changes for select element. This directive works for both FormsModule
and ReactiveFormsModule
.
Find the steps to use
<select>
element for single selection.
Step-1: Import
ReactiveFormsModule
using imports
metadata of @NgModule
decorator in application module.
Step-2: Create an array of data that will be displayed in
<select>
dropdown. For example we have created a class to store data.
export class Profile { constructor(public prId:string, public prName:string) { } }
Profile
class.
allProfiles = [ new Profile('dev', 'Developer'), new Profile('man', 'Manager'), new Profile('dir', 'Director') ]
<select>
element. Here we will iterate our array allProfiles
to populate data in dropdown. To assign value to option
element, we need to use ngValue
property. We can assign our object to ngValue
using property binding. Find the code snippet to use <select>
element.
<select formControlName="profile"> <option [ngValue]="null" disabled>Choose your profile</option> <option *ngFor="let prf of allProfiles" [ngValue]="prf"> {{ prf.prName }} </option> </select>
formControlName
which will store selected value.
Step-4: On form submit we can fetch the value of selected data using the
FormGroup
instance i.e. userForm
.
let newUser: User = this.userForm.value;
<select>
element we have populated complete object of Profile
class. So when we select data, we get complete Profile
object. We can also fetch selected value using userForm.get('profile')
.
Multiple Select Option
Angular provides following directive to handle multiple selection option.SelectMultipleControlValueAccessor
FormsModule
and ReactiveFormsModule
. To enable multiple selection, we need to use multiple
attribute as <select multiple>
. Find the steps to use it.
Step-1: Create an array of data that will be populated in multiple select option. For example we have a class as follows.
export class Technology { constructor(public techId:number, public techName:string) { } }
Technology
class.
allTechnologies = [ new Technology(100, 'Java'), new Technology(101, 'Angular'), new Technology(102, 'Hibernate'), new Technology(103, 'Spring') ]
<select multiple>
element.
<select multiple formControlName="technologies"> <option *ngFor="let tech of allTechnologies" [ngValue]="tech"> {{ tech.techName }} </option> </select>
formControlName
which will store all selected values. The elements from allTechnologies
array will be populated and a user can select multiple values at one time. The selected values can be fetched using userForm.get('technologies')
.
Default Selected Value
We can set values to select options as usual by usingsetValue
or patchValue
of FormGroup
.
setDefaultValues() { let user = new User(); user.userName = "Narendra Modi"; user.profile = this.allProfiles[2]; user.technologies = [ this.allTechnologies[1], this.allTechnologies[3] ]; this.userForm.setValue(user); }
Select Option Validation
For validation of select element, we need to use either validation attribute such asrequired
as select element attribute or Validators
such as Validators.required
in FormControl
. Suppose we want to validate select box using Validators
. Our select box form control will look like as below.
this.userForm = this.formBuilder.group({ profile: [null, [ Validators.required ] ], technologies: [null, [ Validators.required ] ], ------ });
<select formControlName="profile"> <option [ngValue]="null" disabled>Choose your profile</option> <option *ngFor="let prf of allProfiles" [ngValue]="prf"> {{ prf.prName }} </option> </select> <div *ngIf="profile.invalid && userForm.dirty" class="error"> Profile required. </div> <select multiple formControlName="technologies"> <option *ngFor="let tech of allTechnologies" [ngValue]="tech"> {{ tech.techName }} </option> </select> <div *ngIf="technologies.invalid && userForm.dirty" class="error"> Technologies required. </div>
compareWith in Select Option
Angular uses object identity by default to select options but in some scenarios there can be discrepancies when object values remains same but object identity is changed. To handle such scenarios, angular providescompareWith
input with <select>
and <select multiple>
element. We will bind a method with compareWith
to change default logic of object comparison. Find the code snippet.
<select multiple formControlName="technologies" [compareWith]="compareTech"> <option *ngFor="let tech of allTechnologies" [ngValue]="tech"> {{ tech.techName }} </option> </select>
compareTech(t1: Technology, t2: Technology): boolean { return t1 && t2 ? t1.techId === t2.techId : t1 === t2; }
<select multiple>
element using setValue
or patchValue
and we can see that our compareTech()
method will be called.
Select Option Change Event
Select option change event is called when selection of item is changed. Change event is used as follows.<select formControlName="profile" (change)="onProfileChange()"> --- </select>
onProfileChange() { let profile: Profile = this.profile.value; console.log('Profile Changed: ' + profile.prName); }
Complete Example
Find the project structure of our example.angular-demo | |--src | | | |--app | | | | | |--domain | | | | | | | |--profile.ts | | | |--technology.ts | | | |--user.ts | | | | | |--services | | | | | | | |--user-service.ts | | | | | |--user-component | | | | | | | |--user.component.ts | | | |--user.component.html | | | |--user.component.css | | | | | |--app.component.ts | | |--app.module.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
user.component.ts
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { UserService } from '../services/user-service'; import { Profile } from '../domain/Profile'; import { Technology } from '../domain/technology'; import { User } from '../domain/user'; @Component({ selector: 'app-template', templateUrl: './user.component.html', styleUrls: ['./user.component.css'] }) export class UserComponent implements OnInit { isValidFormSubmitted = false; allProfiles: Profile[]; allTechnologies: Technology[]; userForm: FormGroup; constructor(private formBuilder: FormBuilder, private userService: UserService) { } ngOnInit(): void { this.userForm = this.formBuilder.group({ profile: [null, [Validators.required]], userName: ['', [Validators.required]], technologies: [null, [Validators.required]] }); this.allProfiles = this.userService.getPofiles(); this.allTechnologies = this.userService.getTechnologies(); } get profile() { return this.userForm.get('profile'); } get userName() { return this.userForm.get('userName'); } get technologies() { return this.userForm.get('technologies'); } onFormSubmit() { this.isValidFormSubmitted = false; if (this.userForm.valid) { this.isValidFormSubmitted = true; } else { return; } let newUser: User = this.userForm.value; this.userService.createUser(newUser); this.resetForm(this.userForm); } resetForm(form: FormGroup) { form.reset(); } setDefaultValues() { let user = new User(); user.userName = "Narendra Modi"; user.profile = this.allProfiles[2]; user.technologies = [ this.allTechnologies[1], this.allTechnologies[3] ]; this.userForm.setValue(user); } onProfileChange() { let profile: Profile = this.profile.value; console.log('Profile Changed: ' + profile.prName); } compareTech(t1: Technology, t2: Technology): boolean { console.log(t1.techId + '-' + t2.techId); return t1 && t2 ? t1.techId === t2.techId : t1 === t2; } }
<h3>User Information</h3> <p *ngIf="isValidFormSubmitted" class="success"> Form submitted successfully. </p> <form [formGroup]="userForm" (ngSubmit)="onFormSubmit()"> <table> <tr> <td>Select Profile: </td> <td> <select formControlName="profile" (change)="onProfileChange()"> <option [ngValue]="null" disabled>Choose your profile</option> <option *ngFor="let prf of allProfiles" [ngValue]="prf"> {{ prf.prName }} </option> </select> <div *ngIf="profile.invalid && userForm.dirty" class="error"> Profile required. </div> </td> </tr> <tr> <td>Name:</td> <td> <input formControlName="userName"> <div *ngIf="userName.invalid && userForm.dirty" class="error"> User Name required. </div> </td> </tr> <tr> <td>Select Technologies: </td> <td> <select multiple formControlName="technologies" [compareWith]="compareTech"> <option *ngFor="let tech of allTechnologies" [ngValue]="tech"> {{ tech.techName }} </option> </select> <div *ngIf="technologies.invalid && userForm.dirty" class="error"> Technologies required. </div> </td> </tr> <tr> <td colspan="2"> <button>Submit</button> <button type="button" (click)="setDefaultValues()">Set Default</button> <button type="button" (click)="resetForm(userForm)">Reset</button> </td> </tr> </table> </form>
table { border-collapse: collapse; } table, th, td { border: 1px solid black; } .error { color: red; } .success { color: green; }
export class Profile { constructor(public prId:string, public prName:string) { } }
export class Technology { constructor(public techId:number, public techName:string) { } }
import { Profile } from './profile'; import { Technology } from './technology'; export class User { userName: string; profile: Profile = null; technologies: Technology[]; }
import { Injectable } from '@angular/core'; import { Profile } from '../domain/profile'; import { Technology } from '../domain/technology'; import { User } from '../domain/user'; @Injectable({ providedIn: 'root' }) export class UserService { getPofiles(): Profile[] { let profiles = [ new Profile('dev', 'Developer'), new Profile('man', 'Manager'), new Profile('dir', 'Director') ] return profiles; } getTechnologies(): Technology[] { let technologies = [ new Technology(100, 'Java'), new Technology(101, 'Angular'), new Technology(102, 'Hibernate'), new Technology(103, 'Spring') ] return technologies; } createUser(user: User) { //Log user data in console console.log("User Name: " + user.userName); console.log("Profile Id: " + user.profile.prId); console.log("Profile Name: " + user.profile.prName); for (let i = 0; i < user.technologies.length; i++) { console.log("Technology Id: " + user.technologies[i].techId); console.log("Technology Name: " + user.technologies[i].techName); } } }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-template></app-template> ` }) export class AppComponent { }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { UserComponent } from './user-component/user.component'; import { UserService } from './services/user-service'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule ], declarations: [ AppComponent, UserComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Find the print screen of the output.

References
SelectControlValueAccessorSelectMultipleControlValueAccessor