Combo Box In Angular

sharang ukidve
4 min readDec 1, 2018

--

First thing first, What is a combo box?

A combobox is an input field with a dropdown for available options. The users can either select a value from the dropdown or type in the input field.

The beginning — creating an angular application:

ng new combobox-example

Now, let’s create a new component.

We can either do that with the help of Angular CLI or just create it manually.

The HTML Template:

The HTML for the combo-box is quite easy to understand. Copy and paste the following code into the newly created component’s HTML file.

<div class="combobox">
<input type="text" placeholder="Select one..."
class="combobox-input" [(ngModel)]="inputItem"
(ngModelChange)="getFilteredList()" (keyup)="onKeyPress($event)"
(blur)="toggleListDisplay(0)" (focus)="toggleListDisplay(1)"
[ngClass]="{'error': showError}">
<i *ngIf="showError" class="error-text">Invalid Selection.</i>
<div class="combobox-options" *ngIf="!listHidden">
<list-item *ngFor="let item of filteredList;let i = index"
[ngClass]="{'selected': i===selectedIndex}"
(click)="selectItem(i)">
{{item}}
</list-item>
</div>
</div>

A couple of things to note here:

(ngModelChange) is used to detect changes on the variable bound to the input.

list-item is not another component, it is just a custom tag.
If Angular throws an error, just include CUSTOM_ELEMENTS_SCHEMA in your module’s schemas.
You can find more information on the same here and here.

The CSS:

Copy the following styles into the component’s CSS file.

.combobox {
margin: auto;
}
.combobox-input {
width: 330px;
border: none;
padding: 10px;
font-size: 18px;
border-bottom: 2px solid rgb(165, 112, 112);
}
.combobox-input:focus {
outline-style: none;
}
.combobox-options {
position: absolute;
text-align: center;
background-color: white;
width: 350px;
max-height: 200px;
overflow-y: auto;
box-shadow: 2px 2px 2px 2px #ccc;
z-index: 1;
}
.selected {
color: cornsilk;
background-color: rgb(165, 112, 112);
}
list-item {
display: block;
font-size: 18px;
padding: 10px;
}
list-item:hover {
background-color: grey;
color: white;
}.error-text {
color: rgb(165, 112, 112);
}
.error {
border: 1px solid red;
}

The TypeScript:

Finally, coming to the working of the combobox, copy and paste the following code in the component’s .ts file.

import { Component, OnInit, Input } from '@angular/core';@Component({
selector: 'app-combo-box',
templateUrl: './combo-box.component.html',
styleUrls: ['./combo-box.component.css']
})
export class ComboBoxComponent implements OnInit { @Input() list: string[]; // two way binding for input text
inputItem = '';
// enable or disable visiblility of dropdown
listHidden = true;
showError = false;
selectedIndex = -1;
// the list to be shown after filtering
filteredList: string[] = [];
constructor() { } ngOnInit() { this.filteredList = this.list;
}
// modifies the filtered list as per input
getFilteredList() {
this.listHidden = false;
if (!this.listHidden && this.inputItem !== undefined) {
this.filteredList = this.list.filter((item) => item.toLowerCase().startsWith(this.inputItem.toLowerCase()));
}
}
// select highlighted item when enter is pressed or any item that is clicked
selectItem(ind) {
this.inputItem = this.filteredList[ind];
this.listHidden = true;
this.selectedIndex = ind;
}
// navigate through the list of items
onKeyPress(event) {
if (!this.listHidden) {
if (event.key === 'Escape') {
this.selectedIndex = -1;
this.toggleListDisplay(0);
}else if (event.key === 'Enter') {
this.toggleListDisplay(0);
}else if (event.key === 'ArrowDown') {
this.listHidden = false;
this.selectedIndex = (this.selectedIndex + 1) % this.filteredList.length;
if (this.filteredList.length > 0 && !this.listHidden) {
document.getElementsByTagName('list-item')[this.selectedIndex].scrollIntoView(); }
} else if (event.key === 'ArrowUp') {
this.listHidden = false;
if (this.selectedIndex <= 0) {
this.selectedIndex = this.filteredList.length;
}
this.selectedIndex = (this.selectedIndex - 1) % this.filteredList.length;
if (this.filteredList.length > 0 && !this.listHidden) {
document.getElementsByTagName('list-item')[this.selectedIndex].scrollIntoView();
}
}
}
}
// show or hide the dropdown list when input is focused or moves out of focus
toggleListDisplay(sender: number) {
if (sender === 1) {
this.listHidden = false;
this.getFilteredList();
} else {
// helps to select item by clicking
setTimeout(() => {
this.selectItem(this.selectedIndex);
this.listHidden = true;
if (!this.list.includes(this.inputItem)) {
this.showError = true;
this.filteredList = this.list;
} else {
this.showError = false;
}
}, 500);
}
}
}

Explanation:

The data list which we want to display in the Combobox options is passed with @Input() to the Combobox component.

filteredList is used to display the available choices to the user. Initially, we set it to the entire list and later we modify it as per the user input.

getFilteredList() function is called each time when the ngModel i.e. inputItem variable is modified ( whenever the text in the input changes).

selectItem() is used to highlight the currently selected option.

onKeyPress() is called to select list options whenever the input field is focused and Up and Down arrow keys are pressed. Also, whenever Enter is pressed, the highlighted item is selected.

toggleListDisplay() is used to show or hide the options of the Combobox.
Note that the setTimeout() function is used so that if the user clicks an option, that option is selected before the list is hidden.

And a few more things to take care of:
* For ngModel to work, we need to import the FormsModule in the AppModule.
* The data list is to be passed as input to the Combobox component from the parent component.

Stackblitz demo:

--

--