Friday 2 March 2018

Create angular autocomplete - Angular 2/5

Angular 2/5AutoComplete

In this article we will be discussing about creating an autocomplete using angular. I am not using any external libraries to achieve this, we will create it from scratch. We are going to create a simple text box, user can type country names and accordingly it will list matching country names.

AutoComplete component.


Check below autoCompleteComponent code.

autoCompleteComponent


import { Component, OnInit } from '@angular/core';
import { countryList } from '../../services/countryList';

@Component({
    selector: 'auto-complete',
    templateUrl: '../autoComplete/autoComplete.html'
})

export class autoCompleteComponent {
    countrylist: any[];
    searchTerm: string = '';
    filteredResult: any[] = [];


    constructor(private _countryService: countryList) { }

    ngOnInit() {
        //get country list
        //Ideally call an api and get it
        this.countrylist = this._countryService.getCountrylist();
    }

    //filter country list based on user input
    filterCountryList() {
        if (this.searchTerm && this.searchTerm !== '') {
            let _term = this.searchTerm.toLowerCase();
            this.filteredResult = this.countrylist.filter(function (el: any) {
                return el.name.toLowerCase().indexOf(_term.toLowerCase()) > -1;
            });
        } else {
            this.filteredResult = [];
        }
    }

    //set selected country
    selectCountry(name) {
        this.searchTerm = name;
        this.filteredResult = [];
    }

}

Here this.countrylist is nothing but a list of country names. I have added those list in countryList service file. You can replace this list with your data (You can make an API call and get the data as well). In the filterCountryList method, we are filtering the data based on user input. selectCountry method will set the user selected country. Now let us check the html code.

Html


<div class="typeHead">
    <input type="text" [(ngModel)]="searchTerm" placeholder="Country name" (keyup)="filterCountryList()">
    <ul *ngIf="filteredResult.length>0">
        <li *ngFor="let c of filteredResult" (click)="selectCountry(c.name)">
            <span>{{c.name}}</span>
        </li>
    </ul>
</div>

Here on keyup we are invoking filterCountryList method. Setting the user selected country by using selectCountry method. We are done. Don't forgot to import the dependencies in the main module. Start typing any country name you can see the suggested countries are getting populated.

If you want to know more about filtering data using pipes with different examples, check this link - Simple and advanced search using pipe in Angular

Now we need to write a code which should close the autocomplete box on user clicks outside. Check below updated code for that.

Add host to component


@Component({
    selector: 'auto-complete',
    templateUrl: '../autoComplete/autoComplete.html',
    host: {
        '(document:click)': 'closeAutocompleteDiv($event)',
    }
})

Update constructor


constructor(private _countryService: countryList, el: ElementRef) {
        this._el = el;
    }

closeAutocompleteDiv method is to close the autocomplete box.


closeAutocompleteDiv(event) {
        let _event = event.target;
        let _in = false;

        do {
            if (_event === this._el.nativeElement) {
                _in = true;
            }
            _event = _event.parentNode;
        }
        while (_event);
        if (!_in) {
            this.filteredResult = [];
        }
    }

Check below full component code for clear idea.


import { Component, OnInit, ElementRef } from '@angular/core';
import { countryList } from '../../services/countryList';

@Component({
    selector: 'auto-complete',
    templateUrl: '../autoComplete/autoComplete.html',
    host: {
        '(document:click)': 'closeAutocompleteDiv($event)',
    }
})

export class autoCompleteComponent {
    countrylist: any[];
    searchTerm: string = '';
    filteredResult: any[] = [];
    public _el;

    constructor(private _countryService: countryList, el: ElementRef) {
        this._el = el;
    }

    ngOnInit() {
        //get country list
        //Ideally call an api and get it
        this.countrylist = this._countryService.getCountrylist();
    }

    //filter country list based on user input
    filterCountryList() {
        if (this.searchTerm && this.searchTerm !== '') {
            let _term = this.searchTerm.toLowerCase();
            this.filteredResult = this.countrylist.filter(function (el: any) {
                return el.name.toLowerCase().indexOf(_term.toLowerCase()) > -1;
            });
        } else {
            this.filteredResult = [];
        }
    }

    //set selected country
    selectCountry(name) {
        this.searchTerm = name;
        this.filteredResult = [];
    }
    //close autoComplete box on outside click except this one
    closeAutocompleteDiv(event) {
        let _event = event.target;
        let _in = false;

        do {
            if (_event === this._el.nativeElement) {
                _in = true;
            }
            _event = _event.parentNode;
        }
        while (_event);
        if (!_in) {
            this.filteredResult = [];
        }
    }
}

And here is our countryList service file where we have hardcoded country names.


import { Injectable } from '@angular/core';

@Injectable()
export class countryList {
    countryList: any[] = [
        { "name": "Afghanistan", "code": "AF" },
        { "name": "Ă…land Islands", "code": "AX" },
        { "name": "Albania", "code": "AL" },
        { "name": "Algeria", "code": "DZ" },
        { "name": "American Samoa", "code": "AS" },
        { "name": "AndorrA", "code": "AD" },
        { "name": "Angola", "code": "AO" },
        { "name": "Anguilla", "code": "AI" },
        { "name": "Antarctica", "code": "AQ" },
        { "name": "Antigua and Barbuda", "code": "AG" },
        { "name": "Argentina", "code": "AR" },
        { "name": "Armenia", "code": "AM" },
        { "name": "Aruba", "code": "AW" },
        { "name": "Australia", "code": "AU" },
        { "name": "Austria", "code": "AT" },
        { "name": "Azerbaijan", "code": "AZ" },
        { "name": "Bahamas", "code": "BS" },
        { "name": "Bahrain", "code": "BH" },
        { "name": "Bangladesh", "code": "BD" },
        { "name": "Barbados", "code": "BB" },
        { "name": "Belarus", "code": "BY" },
        etc...............
    ]
    getCountrylist() {
        return this.countryList;
    }
}

I have used scss for styling, you can update the styles for better look and feel.

Css (.scss)


.typeHead{
    input{
        border: none;
        padding: 6px 18px;
        width: 439px;
        border-bottom: 1px solid #134f5c;
    }
    ul{
        background: #fff;
        width: 439px;
        li{
            list-style-type:none;
            padding: 6px 18px;
            margin-left: -40px;
            cursor: pointer;
        }
        :hover{
            background:#ccc;
        }
    }
}

We can implement the same using HTML 5 + Angular concept as well with very minimum codes - Angular autocomplete using HTML 5 - Angular 2/5

In this article we have discussed about creating an autocomplete using angular from scratch.

Angular 2/5AutoComplete

Related Info

1. Http calls in Angular with simple examples.

2. 5 different ways for conditionally adding class - Angular.

3. Angular 2/4 routing with simple examples.

4. Reusable flashy message using Angular 4

5. Login authentication flow using angular auth guard - Angular 5

1 comment :