Autocomplete input text is one of the crucial features in web applications to provide better user experience
In this quick tutorial, we try to build an autocomplete text input using rxjs library.
Let's first generate an empty npm package
npm init -y
And we will add 3 pages :
- index.html
- style.css
- index.js
You find full project in my github repo
This is index.html
<html>
<head>
<title>rxjs autocomplete</title>
</head>
<body>
<div class="container">
<div class="flex-container">
<input
type="text"
class="form-control"
placeholder="Search"
id="text-input"
/>
<ul class="list-group"></ul>
</div>
</div>
<script src="./index.js"></script>
</body>
</html>
And now let's work on index.js
In this file, we will handle changes in text input and then send http request to the server and finally, we display results
First we are going to mock an API to be able to send HTTP requests according to the text typed and receive responses with the help of json-server.
npm i json-server
Then we create a db.json file with some data.
{
"Clubs":[
{"name":"Everton","year":"1878"},
{"name":"Fulham","year":"1879"},
{"name":"Manchester city","year":"1880"},
{"name":"Manchester united","year":"1878"},
{"name":"Tottenham","year":"1882"},
{"name":"Aston villa","year":"1874"},
{"name":"Leeds united","year":"1919"},
{"name":"Liverpool","year":"1892"},
{"name":"Arsenal","year":"1886"},
{"name":"Chelsea","year":"1905"}
]
}
In the package.json file, we add in the scripts tag this command
"server": "json-server db.json --port 3000"
we are allowed then to run the server with:
npm run server
and the server is ready to handle requests.
Now let's move to the next part and install rxjs.
npm i rxjs
We take now the text input to be able to add an event listener to this element and this through fromEvent function (from rxjs) that emits events of a specific type (keyup) coming from the given target in our case text input element(inputTxt).
import { fromEvent} from "rxjs";
let inputTxt = document.getElementById("text-input");
let textChange$ = fromEvent(inputTxt, "keyup");
Now we are listening to inputTxt, whatever key is typed an event will be fired.
In autocomplete feature, usually, we don't send HTTP request for each character, in other words, we wait an amount of time to see if the user add another character to the input, also, we don't send similar requests in a row to get better performance.
Therefore we are going to use the pipe function to apply the required functionality to the observable stream.
map: returns an observable by applying a project function to each value emitted by the source observable. This allows us to retrieve from target event only text value
debounceTime: returns an observable that emits values after only a particular time span in ms has passed without another source emission.
distinctUntilChanged: returns an observable that emits values only if it is different from the previous one.
mergeMap: returns an observable that emits values by applying a specific function, it is the combination between mergeAll and map. The function to be applied in our case is the HTTP request made using fetch API that takes URL endpoint input and sends back a JSON response. Fetch function is called only if the value is not falsy else an empty array will be sent back.
Understanding map,mergeMap,switchMap,concatMap
import {
debounceTime,
map,
distinctUntilChanged,
mergeMap,
} from "rxjs/operators";
import { fromEvent, of } from "rxjs";
let autoSuggest$ = textChange$.pipe(
map((e) => e.target.value),
debounceTime(1000),
distinctUntilChanged(),
mergeMap((value) =>
value ? fetch(url + value).then((response) => response.json()) : of([])
)
);
Now let's handle this event by subscribing to textChanges$ observable.
autoSuggest$.subscribe((v) => {
let list = document.getElementsByClassName("list-group")[0];
while (list.firstChild) {
list.removeChild(list.firstChild);
}
for (let club of v) {
let li = document.createElement("li");
let textNode = document.createTextNode(club.name);
li.setAttribute("class", "list-group-item list-group-item-primary");
li.appendChild(textNode);
list.appendChild(li);
}
});
We removed first previous results, we created a li element, then we iterated through data and created a text node where club names will be displayed, this text node is added to the li element which is added also to the list element.
To be able to run this little project we need a bundling tool, one of the easiest to configure is Parcel.
Then we install parcel.
npm i parcel-bundler
and we added parcel command in script tag with project entry point which is index.html
"dev": "parcel index.html"
Then we can run input autocomplete, and don't forget to launch our JSON server
npm run server
npm run dev