Basic input autocomplete with rxjs

Hssan Bouzlima - Mar 21 '21 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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"}
]
}

Enter fullscreen mode Exit fullscreen mode

In the package.json file, we add in the scripts tag this command

    "server": "json-server db.json --port 3000"
Enter fullscreen mode Exit fullscreen mode

we are allowed then to run the server with:

npm run server
Enter fullscreen mode Exit fullscreen mode

and the server is ready to handle requests.

Now let's move to the next part and install rxjs.

npm i rxjs 
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode
let autoSuggest$ = textChange$.pipe(
  map((e) => e.target.value),
  debounceTime(1000),
  distinctUntilChanged(),
  mergeMap((value) =>
    value ? fetch(url + value).then((response) => response.json()) : of([])
  )
);
Enter fullscreen mode Exit fullscreen mode

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);
  }
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

and we added parcel command in script tag with project entry point which is index.html

"dev": "parcel index.html"
Enter fullscreen mode Exit fullscreen mode

Then we can run input autocomplete, and don't forget to launch our JSON server

npm run server
npm run dev
Enter fullscreen mode Exit fullscreen mode

Full github repo

. . . . . . . . . . . .