Software engineering

Reactive Autocomplete With Polymer and RxJS

Aug29th, 201511:53 pm

I. Intro

In my first blog post I will try to make basic introduction to Polymer and RxJs.

A) What is polymer?

Polymer is library for creating web
components. Web components are reusable elements, containers with their own isolated api, templates and style. You may think about
them like about AngularJs directives. Difference between angularjs and polymer is fundamental. Polymer is a library which
targets one task when angular is a framework for building whole apps. Moreover it is built on top of web
components. You can of course use polymer inside
angular project and you can build app based only on web components but those tools are targeting different problems
in web development.

B) What is Rx?

Rx is, according to main rx page, “an API for asynchronous programming with observable streams”.

C) Assumptions

In following example we will try to create component for searching. After typing we should get list of best suggestions.
We would like it to be reusable, it should have
his own encapsulated style and what most important API. This is why we have chosen Polymer. Search elements seems
simple but they have some interesting corner cases. Some of them:

User types very quickly some phrase for example: ‘polymer tutorial’. Our component should be intelligent enough
to
know that it should send only one request to server, with param ‘polymer tutorial’, and not sending requests after
each typed by user letter.
This seems simple. Maybe we need some timeout. Probably we don’t need to incorporate external library like Rx to
achieve this.

User types some phrase: ‘polymer tutorial’, then request goes to a server, then(before or after first request
finishes, that’s not important) user add more letters to phrase for
example he adds new words like ‘for beginners’ but in a moment he realises that he is polymer king and he doesn’t
need this ‘for beginners’ part, so he quickly deletes it. Our search component should know that people make
mistakes
and it should not make another request since it would be with same param: ‘polymer tutorial’.

User types some phrase: ‘polymer’, request goes to a server, then(before first request finishes) he adds new
phrase
to existing one: ‘tutorial’. How we could guarantee that the second request with param: ‘polymer tutorial’
will
finish after the first one(with param ‘polymer’) and user will get expected suggestions?

Especially last case seems to be non trivial. And this is case where RX fits very well, as we see later, concept of
observables(maybe more intuitive name is ‘stream’) gives us abstraction for dealing with such problems.

II. Setup

I assume that you have git, Node, and Bower installed.

A) Clone from github

Before cloning project install polyserve- it will run localhost server for us:

B) Create project from scratch

Other option to start is to create directory named ‘search-component’, then download and run seed-element which is default template for building
polymer web components. Follow instructions in Polymer docs till this section:
https://www.polymer-project.org/1.0/docs/start/reusableelements.html#develop-and-test
I encourage you to preview this template and after running (in /search-component directory where you should place
unzipped seed-element files)

1

bower install

and then:

1

polyserve

you should have server on port 8080 started.
When you go to http://localhost:8080/components/seed-element/ you should see default docs and demo page for
seed-element component. You can read how should be use, how its api looks like and preview
in action.

So after review, we can now safely delete seed-element.html, and create new file search-component.html.
It should look like this:

You can find these info in docs but I will repeat them anyway. Registering an element associates name of the element
with a prototype so you can add properties and methods to your component. As you see function Polymer(arg) takes as
argument object that defines your element’s prototype. We need to import polymer, then in script we can initialize our component. We do
this by specifying its name, must contain “-”. This is because HTML5 specification which says that custom components should
have “-” and native html components don’t have to.

Next we can see ready which is lifecycle callback and it means that when your component is loaded then function
from ready will be fired. To see this component in action go to demo/index.html and look how it is initialized.

So we have got our component but it is invisible. We can add local DOM which will be encapsulated.
To do this we have to wrap it inside <dom-module> with id the same as name
defined inside Polymer(args). Inside dom-module we can have <script> tags and <template> tags as well. In <script>
we will define logic and behavior and in <template> we will define…html template.

Before we go any further we can review another feature of components: properties. One of the tasks of properties is to
make your component generic. They are values you can pass to your component when you initialize your component on page. You can
set type of property, its default value and even you can observe changes of it. Properties can be used also for
data-binding as you will see later.

In our example we can extract already some property, we could parameterize placeholder in input tag. So this is how
you define your property:

As you can see we set the type (String) in declaration of inputPlaceholder. This tells polymer how to deserialize
passed value. For us it is hint how to pass values to component. Value attribute specifies default value if none
would be passed to inputPlaceholder.

Next we need to declare property for keeping all elements which we will possibly get as a result of search. I will
give that property name searchResults, its type would be an array, and default value- array with 3 elements just to
simulate some results.

The we would like to show those results somehow. To do it we have to use template tag which we specify as
‘reapeatable’ part of template. This is the way to iterate over collection in polymer. You specify template tag,
telling that it is dom-repeat element, you pass collection to items property and then you can do with item what you
want.

Here we are creating observable(or stream as you wish). Observable is mix of 2 design patterns known from software
engineering: iterator and observer. Why we need this? In age of big data, big doesn’t only mean huge amount. It means
also different sources of data. You might treat big file as data, database with tables and rows as data,
social media notifications and events created by user as data. It would be great to treat those data in similar way,
to have some abstraction which would help us dealing with such big amount/vast sources of data.We know already
something what could be useful: Iterator is
simple design pattern: take a collection(no matter what collection) and give me the next element of it as
long as it has next element. This is nice, we want to use something like iterator for examples of data
mentioned above, without worring about kind of data, that’s great abstraction. You might think about Iterable collection
as data producer and you asking for next element as consumer. There is one big problem: iterator doesn’t know about
notion of time. It works only for ‘synchronous’ collections. This is unacceptable since we are dealing with web
related problems. Moreover iterator throws an error if sth unpredictable or unacceptable happens. This is not
‘happy path’, where we could forget about tedious problems.

But there is observer pattern, pretty similar
thing. You give a callback to data producer and it calls you, observes changes. And this repeats after…after what?
This is one of the missing factors, there is no automatic way to saying producer: no more data, no more
observations! Moreover observer pattern has a lot of
downsides, it breaks good software engineering principles like encapsulation and so on.
Those 2 patterns are about the same thing: sending data to consumer. And data means: whatever data. List of ints,
collection of events, chunks of some big file. But they were not connected with each other. They were not
sufficient with they basic form(iterator only for synchronous collections). And this is why observable was created.
To mix both observator and iterator, and use it for asynchronous data. In observable we can get the next element.
Elements in observable may occur asynchronously. Producer can say: that’s all I have and then method onComplete(),
implemented by us to match our needs will be fired. Same with onError().
We can think about Observable as a timeline with max 3 things: event, error and completion. On diagram below there
is no error, completion is marked with vertical line, events are letters marbles and there is error marked with X.

—-e—e–e—–e-x-|—>

So how it is working: user is typing something for example ‘polymer’, how would our observable look like?

—e—e—e—e—e—e—e—>

Where ‘e’ is event binded to our stream. Now when we have got some abstraction like observable which should have
main feature of Iterator pattern: give me next element!

subscribe() subscribes observer to an observable. This is way to mix iterator with observer. As you see it might take
3 parameters. First is function(in this case look for docs, link below) which is fired when taking next element, second is
fired when something goes wrong, and last is function to complete. Subscribe() has an alias: forEach(). Look for
docs for more explicit explanation: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md
For our purposes we can get rid of onError and onComplete functions.

Now we see in our console that we have some events as results of typing on keyboard, and here we discover great
feature of observable: composability. Before we would take next element of observable, we would like to prepare it
somehow to fit our needs. We need to map our stream:

If you are familiar with any functional programming api this is probably obvious for you. If you are not familiar
with any of those apis, then probably it is obvious too ;). Filter takes as parameter function. This function
must return Boolean and it takes as parameter each element of our stream. If result is true it lets
this element go to new filtered observable, else the element is popped out from the observable.
So we managed first problem, mentioned on beginning of this text.

Now lets solve one of our initial problems with search. User types very quickly ‘polymer’, we don’t want to call
server for each time as we would do in existing solution. When typing, you can see in console that each typed letter
is taken in subscribe().

How is it working?
After map(), and filter, when user types ‘polymer’ very quickly we have observable (and dash ‘-’ means 100ms in our
timeline):

–po-pol-poly-polym-polyme-polymer- - ->

debounce(ms) says: ‘I will go further if time in ms specified in my parameter will pass, after event in observable
occurs’. After ‘po’ there were 100ms, after ‘pol’ the same, and so on, so debounce is saying ‘Hold your horses! When
specified time will pass I will take current value from observable.’

After typing ‘polymer’ 300ms passed so resulting observable after debounce looks like this:

It is bounded to an event ‘keyup’, so when uses types arrow it will be added to resulting observable. It would be
useless for our purposes since we are interested only in changes in input field. We don’t want this kind of data, we
are interested only when text in input field actually changes.
Moreover think about situation
described above in paragraph C). Users types ‘polymer’ and request go to server then he adds ‘for beginners’, but
before request goes (so before 300ms specified in debounce), user deletes this phrase ‘for beginners’ and he is left
with ‘polymer’ as before typing. So he already made a request with term ‘polymer’ we don’t want to make it one
more time, it would be not necessary. Rxjs gives us a function distinctUntilChanged() which will filter out element
of an observable if is is the same as previous element. It will solve those 2 problems described in this paragraph.

How flatMap() works? Similarly to map(). It applies a function to each element in observable. This function returns new
observable or Promise(this is our case) then it fires it, and merge/flatten resulted elements to final/resulted
observable. To show simple result try this code, create simple script file or even use JsBin fo this demo.
We will use interval() method:

Back to our autocomplete. We used flatMapLatest() here. Why? Because one of the biggest problem of our autocomplete
is that we don’t have guarantee that result of first sent request will return before result of request sent little
later. flatMapLatest() works pretty similar to flatMap() except when new item is emitted by original observable
(source), Observable that was generated from the previously-emitted item will disappear, flatMapLatest() will
unsubscribe from it and it will begin only mirroring the current one, the latest. As you see non trivial problem
solved with one liner. Try commenting debounce() and takeUntilCHanged() and play with flatMap and flatMapLatest(),
you will get the difference for sure.

This is mostly end of using RxJs in our example. I encourage you to look at the docs, they are
great source of knowledge, written in easy to understand way. There is also a lot of other examples which would help
you understand concept of observables.

V. Back to Polymer again

Main RxJs part is finished, now lets polish this component a little bit and make it useful.

In definitions of properties we declared value ‘searchResult’ which is an array. We should populate it with results,
after typing by user new phrases. To do this we need to use polymers ‘set’ method. Btw Polymer has its own
api for updating properties which are arrays and you can find those methods in docs:

this.set(‘nameOfProperty’, value) sets value of sets result to second value of resulted observable element, why second value?
Because wikipedia sends back response in this form, where 2. value is array of resulted elements. So now you should
see how our search input works.
BTW You can also find methods for array mutation:
https://www.polymer-project.org/1.0/docs/devguide/properties.html#array-mutation

Now we should parameterize it a little bit. So for example debounce method can take parameter from outside. Value of
debounce could be provided by user who will use our component. We will call it ‘timeout’ because debounce is
reserved name in polymer, btw It is name of method which does similar thing.

Now we can add some polymer sugar to our component. Lets make it more ‘material design’. Go to https://elements.polymer-project.org/
and browse the available components. We will use paper-input. Download it:

...Polymer({is:'search-component',properties:{/** * `inputPlaceholder` indicates the placeholder of search input */inputPlaceholder:{type:String,value:"Default placeholder text"},/** * `searchResults` are the results of searching */searchResults:{type:Array,value:[],observer:"_resultsChanged"},/** * `timeout` is time in ms after which search request will be send to server */timeout:{type:Number,value:500},/** * `minLength` minimal length of input value needed to make request to server */minLength:{type:Number,value:2},/** * `getRemoteSuggestions` function returning promise. Here you should specify ajax call to server. This * function should take search term as parameter */getRemoteSuggestions:{type:Object}},...

Check the results, you can build your component’s documentation with no trouble using comments in appropriate places. The
same with sample usage of your component.

This component could be extended even more, to work not only with remote collections but I will finish here because this post
has grown a little to much.
You can find repo with this project on my github(branch: final-version):