Tutorial: Creating an Angular2 Autocomplete

Before we begin I just want to say that I’m not going to use any third-party component for this tutorial, I’m going to create an autocomplete from scratch, my goal is to just make it show the suggestions and allow the user to select one, but if you need more advanced features feel free to comment, I’ll do what I can to help you.
With that in mind, let’s start by creating our class, just copy/paste the code bellow:
export class AutocompleteComponent { public query = ''; public countries = [ "Albania","Andorra","Armenia","Austria","Azerbaijan","Belarus", "Belgium","Bosnia & Herzegovina","Bulgaria","Croatia","Cyprus", "Czech Republic","Denmark","Estonia","Finland","France","Georgia", "Germany","Greece","Hungary","Iceland","Ireland","Italy","Kosovo", "Latvia","Liechtenstein","Lithuania","Luxembourg","Macedonia","Malta", "Moldova","Monaco","Montenegro","Netherlands","Norway","Poland", "Portugal","Romania","Russia","San Marino","Serbia","Slovakia","Slovenia", "Spain","Sweden","Switzerland","Turkey","Ukraine","United Kingdom","Vatican City"]; public filteredList = []; public elementRef; constructor(myElement: ElementRef) { this.elementRef = myElement; } } |
As you can see I’ve already added the data to my class, as I’m trying to make it simple it’s just an array of countries, but of course in a real case scenario the data would come from your database or via web service, also it wouldn’t be placed inside the component, it would be passed to it, this way the component could be reused with other data in other parts of the application.
I’ve also created the variables query
and filteredList
, the first one will hold the string typed by the user, and the second is where I’m going to store the suggestions being displayed by the component, its value will constantly change as the user types on the input. There is also the constructor where I’m setting the elementRef
value, but I’ll get to that variable in a moment.
Now let’s create the methods to filter the data and select the item:
filter() { if (this.query !== ""){ this.filteredList = this.countries.filter(function(el){ return el.toLowerCase().indexOf(this.query.toLowerCase()) > -1; }.bind(this)); }else{ this.filteredList = []; } } select(item){ this.query = item; this.filteredList = []; } |
These two functions are very straightforward, the filter()
function uses the query
variable to filter the countries
, then it stores the result in the filteredList
.
The select()
is even simpler, I’m just assigning the selected item to the query variable in order to make it appear on the input, and to make the suggestions list disappear I’m removing everything from the filteredList
.
Ok, we already have the two most important functions to make our component work, now we can create the template:
@Component({ selector: 'autocomplete', template: ` <div class="container" > <div class="input-field col s12"> <input id="country" type="text" class="validate filter-input" [(ngModel)]=query (keyup)=filter()> <label for="country">Country</label> </div> <div class="suggestions" *ngIf="filteredList.length > 0"> <ul *ngFor="#item of filteredList" > <li > <a (click)="select(item)">{{item}}</a> </li> </ul> </div> </div> ` }) |
Note that I’ve used some different css classes here, some of them I created myself and some are from materialize, if you want to use them just add to your project the link to their css and js files.
At this point your autocomplete is already working, if you run your project you’ll see a result similar to the gif at the beginning of this tutorial, but there’s still a small problem we have to fix, note that once the suggestions list shows up you have to either erase the text from the input or select one to make the them disappear. The problem is that the user should be able to get rid of the suggestions by clicking anywhere other than the list, and to be able to do that we have to detect clicks outside the our component, more info here.
Remember the elementRef
variable we saw in the constructor? Now we’re going to use it, the follwoing method tells whether the click occured in the component or outside it:
handleClick(event){ var clickedComponent = event.target; var inside = false; do { if (clickedComponent === this.elementRef.nativeElement) { inside = true; } clickedComponent = clickedComponent.parentNode; } while (clickedComponent); if(!inside){ this.filteredList = []; } } |
The function just gets the clicked component, navigates between its parent nodes and checks if one of them is the elementRef.nativeElement
(which is our component), if it’s true the items from the filteredList
are deleted in order to make the list disappear. Now we have to bind this method to the (document:click)
event, this is how you can do it:
@Component({ selector: 'autocomplete', host: { '(document:click)': 'handleClick($event)', }, template: //TEMPLATE CODE HERE }) |
That’s it!! As I said if you need more features or if you have any doubts just leave a comment, I’ll be glad to help!
That’s awesome! I was starting doing my own autocomplete and this will help. Don’t you have a github to upload it so people can contribute?
Thanks bro! If you need help with your autocomplete don’t hesitate to ask. Here’s the link to the project on GitHub: https://github.com/leonardohjines/angular2-autocomplete
Hi,I want to make an autocomplete in which if i type the city automatically its state should come above it…like cityname and just above it like a superset will be state…
thanks
Nice and simple, taught me a few things, thanks for that!
Actually the autocomplete didn’t work properly for me.
To make it work I replaced this
with this:
It’s possible to upgrade your example to mak an multiselect autocomplete as an email ?
Thanks for the suggestion! Here’s the tutorial: http://4dev.tech/2016/06/adding-the-multiselect-feature-to-an-angular2-autocomplete/
Thank you for taking your time explaining the functionality as well. Multiselect functionality would be very helpful if you can upgrade it. Thanks
Just published the multiselect autocomplete tutorial: http://4dev.tech/2016/06/adding-the-multiselect-feature-to-an-angular2-autocomplete/
would be great to have arrow key nav of the options
Keyboard navigation would be very nice! Hier erhalten, nice Job!
I 3rd the request to support keyboard navigation of options… that would be fantastic!
Hi, I have implemented this myself. It isn’t too difficult. First, change template so that the
*ngFor
looks like this:then your
li
should have the attribute[class.complete-selected]="idx == selectedIdx"
(you can change the class name if you want).I also handle the blur event on the input with a handle blur method which looks like this:
finally I added the following code after the bind to the
filteredList
in the filter method (within the ifquery != ""
block of code)Possibly could be better but it seems to work for now.
Hi Daniel,
thanks for the info! Please excuse my limited understanding as I’m still ramping up on Angular2. I wasn’t able to get this working and have some questions:
1) How is the
handleBlur() {...}
function called?I’m assuming you meant adding it to the input field as shown here: Gist of changes made to app.component.ts
2) Regarding the code placed after the bind to the
filterList
..I’m passing the event in the input field to the
filter()
function as as shown here:Gist of changes made to app.component.ts
The code seems to execute without errors but I don’t see the focus moving out of the input field to the items nor any item getting selected when clicking on the downArrow key.
Not sure what I’m missing here. Any help would be highly appreciated!
Hi Cesar,
I have put in a pull request to the github that has the full code changes. I would use the
(blur)="handleBlur"
as this will be called on all blur rather than KeyUp.You would need to have a class in your css that mimics the :hover for the
li
elements. (In my personal project this includes making the background orange). Also, my original comment had the ArrowUp code too (you’ll find it in the github pull request).The git hub pull request is here: https://github.com/leonardohjines/angular2-autocomplete/pull/1
Daniel
Thanks Daniel, works like a charm!
@Cesar, some comments which will hopefully help you.
1. the handleClick is being configured from the host section of the component so I can only presume that you’d have to include something similar there for handleBlur:
2. make sure you add styling for the complete-selected class to your css
Hi, Code is awesome. But i Face the Following Error in keyup, when I tried this code.
I don’t know how to solve this problem. Plz help me.
Try using
el.toString().toLowerCase()
.TypeError: “el.toLowerCase is not a function” means that the typeof “el” doesn’t have a “.toLowerCase” function.
The type of “el” should be “String”.
Example:
Thanks for the tutorial Leonardo. Has anyone had success in adding keyboard navigation ?
Is it possible to make multiple selection from one autocomplete ??
Hi,
did you succeed to make selection from the autocomplete? For me it is not working…
Thank’s for this – you’ve really helped with a custom autocomplete… Question? How about arrow keys support (i.e. up/down & enter to select)? Any suggestions?
how do i use it inside different component where i can add multiple drop-downs for different values?
lets say a user sign up form with country , state as two different dropdowns
Thanks for the tutorial Leonardo!
Based on your project I created my own version with keyboard navigation and option to show all items.
Instead of use Material I used Twitter Bootstrap, but it can be changed to your needs.
For those who are asking for it here is the plunker:
http://embed.plnkr.co/4ZF6Fs/
Also the github project:
https://github.com/limakayo/angular2-autocomplete
Thanks!
Hi,
I am new in Angular2 and I used your Git project for the autocomplete component. After “npm – start ” I get the following errors:
Can you please help me?
Thank You!
Specify the type for public variables and each parameter of select(), handleClick(), and the first parameters function parameters for Array.prototype.filter().
This is working .But this doesn’t show example image. I want to show example image.
so please……How do ????
i write frmAutoComplete.component.ts.
===================================
==================================================
I use this at frmDailyLog.component.ts.
====================================================
=========================================================
please,help me and tell me how to do and change my code?
Make sure you’re using the materialize css classes correctly on your template.
is it possible to make a generic autocomplete component, and pass the country list items to the generic component? and how about making it cascading for state/city selection.
Hi Aseni! This may help you with the generic component:
http://4dev.tech/2016/07/passing-parameters-to-an-angular2-component/
Hi, thanks for this tutorial. It saved me a lot of trouble. I have one question with regard to your code on plunker. In the list of dropdown elements, I want it such that when I use the arrow buttons, I don’t want hover to work and vice versa. At all points either hover or drop down should work . I should be able to switch from hovering with mouse to using the keyboard dropdown and vice verse. I am not sure how to implement this functionality. Thanks!!
I have problem when selecting a list item with the cursor, the input field is not showing the targeted item, but only when some arrow key is pressed first. Is there maybe something else that I need to change as well?
Thanks!
Github file not working. not displaying the selected country in text box.
You have to cancel the (blur) event, both the html attribute and the class method.
So delete (blur)=handleBlur() from tag and the handleBlur() method from AppComponent class.
I changed its style, code for accepting json objects … and many more things but all in all its an component code given – I liked it alot ..
I tried to change the output of the list by changing and by replacing them with and . However when I select an element in my list, it doesn’t fill the input anymore… Any clues on how to do this ?
** the li and ul, replaced by select and option
Hi, Leonardo.
I have trouble:
– the select(item) function not reacted when clicked on array list.
why it is happened?
Can not find issue about one week, thanks
Hi Leonardo,
I’m also running in the trouble of the select(item) function. When I hover over the suggestion and click with my mouse, the ‘query’ does not seem to get updated to fill up the selected country on the input field.
can you help with loading dropdownlist with favourite list of items and user can use from them or search and select from them.
i am getting an error like this
Error TS2339 Property ‘parentNode’ does not exist on type ‘EventTarget’.
I tried to use the ArrowDown (and ArrowUp) functionality described by Daniel:
However, I get an exception “Cannot read property ‘code’ of undefined” when typing in the input field. What have I missed?
it is great work. thank you . please provide your css also.
Have you thought about using templates? I mean I want to bind country object instead of just string i.e. name of the country. For e.g. I want to show country name as well as its flag. Can you give me a start for such option?
Will it be possible to adapt this to ionic2 input field like ion-input?
Thank you guys
Srinivas Guddati
Yes, you can make it work on ionic2 without having to change input to ion-input, I am working on ionic2 and it worked for me like charm 😀
It would be nice if you can share your CSS, many thanks
Hi, the auto-complete works fine… but how do we add a Bold prefix on the shown list… I have seen another example (ionic2-autocomplete) and they are using a pipe for that, but i couldn’t implement it here.
here is the pipe:
How do we use this in this example
Nice but up and down arrow key is not working.
Any help,how to add Keyboard Event select list item using up and down arrow key with scrollbar.
Thanks in Advance.
Hi,
Thks for the tutorial it’s really great. I am neebie in angular 2 and I would to know if it’s possible and hard to improve your code ta pass an array of object instead of an array of string ?
thank u very much
Hi, Thanks for this but i need the exemple with data come from web service plz
Hello,
I want a functionality where I don’t want force selection. The functionality to be implemented is. On input box click, I want a list of options to come up and select from the options if I find the required one. Or else, i want to type in the input box and click save.
Hi,
I am integrating this component into my Angular 5 app.
When I run it, this message appears:
core.js:1350 ERROR Error: Uncaught (in promise): Error: StaticInjectorError[ElementRef]:
I imported it my .ts component: should I import it inside my app.module.ts again?
Best regards.
Hi ,
This is amazing and yet very simple…
Thank u so much..
I am getting below error.
StaticInjectorError[t]:
NullInjectorError: No provider for t!
Error: StaticInjectorError[t]:
StaticInjectorError[t]:
NullInjectorError: No provider for t!
Thank you!
you example help me get started with writing my autocomplete
Hi,
Thank you for this solution it helps me a lot, but i want to call filter() when i just clicks inside input tag without inserting anything it should show me some suggestions. and also want to provide some validations that user should only select value from dropdown, i don’t want to allow user to type anything.
Please suggest me the same