AI Cygnus
1 supporter
RxJs Tutorials - Synchronous Type Ahead ...

RxJs Tutorials - Synchronous Type Ahead - Ex 1.1

Oct 13, 2020

RxJs is a very powerful library. tI helps us to manage event-based and asynchronous data using a common API. It is really helpful but it's indeed a different way of doing things and it can be really hard to understand all of these concepts but once you understand, you will fell in love with these concepts.

To make the learning of RxJs concepts easier I am starting a tutorial series which will be based on https://www.learnrxjs.io/ recipes. Shout out to adamlubek for creating all these recipes. Today we are going to create a type ahead. In part one of this, we are going to work with synchronous data and we will create an asynchronous data typeahead next time. Here we will get a sneak peek into the power of RxJs which provides us with a single API to work with events, async data, streams, etc.

Prerequisites:

  • Basic knowledge of Rxjs

  • Whatis a typeahead

  • Knowledge of Typescript

We list down the functionalities we need for creating synchronous typeahead:-

  • Get the data in the search box

  • Get data from the source

  • Filter the data of source by the data in the search box

  • Debounce events for much efficient data processing

To see the final output of the code you can visit https://www.learnrxjs.io/learn-rxjs/recipes/type-ahead and see what we are trying to achieve.

First, to get data from the search box on each key up event. We have to create a basic HTML structure. Like:-

Get continents
<input id="type-ahead" />
<hr />
<div id="output"></div>

Then to use RxJs we have to convert data from the search box to an observable stream. Now the first question that should come to mind is there an API for creating an event stream? After searching for "Rxjs event API" I found fromEvent (it Creates an Observable that emits events of a specific type coming from the given event target) which is perfect for our use case. We want an event to be emitted each time a keyup event occurs in our type-ahead search box. To do that we do

fromEvent(document.getElementById('type-ahead'), 'keyup')

This creates an observable stream of keyup events on the type-ahead element. Next, we need to convert this event to its target value to do that I start to search for "converting data in Rxjs" so I got results like a map, scan, pluck. We need to apply this transformation to each event which after some research seems can be best done via the map operator. To do this transformation we have to pipe in the map operator on this observable.

fromEvent(document.getElementById('type-ahead'), 'keyup')
.pipe( map((e: any) => e.target.value))

Now again we have to do a transformation and again covert each typed key into its results. So we can do that via filtering an array we can do that via a function:-

const getContinents = keys =>
  [
    'africa',
    'antarctica',
    'asia',
    'australia',
    'europe',
    'north america',
    'south america'
  ].filter(e => e.indexOf(keys.toLowerCase()) > -1);

Now we can use this function using another map function

map(getContinents)

We have added the data we only need to add it to the DOM. Right now we don't want to make any transformation or anything we just want to perform a task without doing anything to the data which is like a side effect. After searching "side effects RxJs" I found the tap operator. Using this we can create side effects and put it properly in the shape in the DOM. To do this we add the tap operator to our pipeline

tap(c => (document.getElementById('output').innerText = c.join('\n')))

After running this still, nothing is working. Oh! I forgot observables are lazy they don't start until no one is listening to them using subscribe(). So, let us subscribe to it.

The typeahead is complete now let us look at our results.

const getContinents = keys =>
  [
    'africa',
    'antarctica',
    'asia',
    'australia',
    'europe',
    'north america',
    'south america'
  ].filter(e => e.indexOf(keys.toLowerCase()) > -1);

fromEvent(document.getElementById('type-ahead'), 'keyup')
  .pipe(
    map((e: any) => e.target.value),
    map(getContinents),
    tap(c => (document.getElementById('output').innerText = c.join('\n')))
  )
  .subscribe();

This seems to be complete now but some careful introspection leads me to think that we are overdoing the filtering operation. I think we can optimize it because when typing really fast then filtering data on each keypress is not optimal and filtering data when the actual data do not change is also not optimal like when we press the CTRL-key. In order to improve upon both of these, we do filtering only when data changes inside the search box and debounce these events every 200ms and after searching for a while I found out a debounceTime operator in Rxjs on the first search and for not filtering id data is not changed I found out the distinctUntilChanged operator. Again adding these to the data pipeline yields us.

const getContinents = keys =>
  [
    'africa',
    'antarctica',
    'asia',
    'australia',
    'europe',
    'north america',
    'south america'
  ].filter(e => e.indexOf(keys.toLowerCase()) > -1);

fromEvent(document.getElementById('type-ahead'), 'keyup')
  .pipe(
    debounceTime(200),
    map((e: any) => e.target.value),
    distinctUntilChanged(),
    map(getContinents),
    tap(c => (document.getElementById('output').innerText = c.join('\n')))
  )
  .subscribe();

I think our type ahead is in a good shape, optimized and we can finish our synchronous Typeahead here.

So you see Rxjs let us do many things and so some pretty powerful things to our data composition pipelines by piping operators in our data. We will soon see many other use cases that will help us understand the power of this library.

You can see the final result here.

Next time we will look at how RxJs helps us when we are dealing with async data in the typeahead.

Enjoy this post?

Buy AI Cygnus a pizza

More from AI Cygnus