Creating an Independent Search Component Using Akita & Angular

Amir Tugendhaft
ITNEXT
Published in
3 min readFeb 8, 2019

--

Recently, I started using Akita as my state storage for my new application.
It ships with a bunch of nice concepts for clearing up the usage of a store in a web application.

  • You have a model that you store inside what’s called an “entity store”.
  • You can fill that “entity store” using an entity API service.
  • You can then query the “entity store” for values using a query service that ships with a bunch of out-of-the-box queries/selectors.

Let’s see how we can use this store to create an independent search component which you can later embed everywhere in your application without having to inject necessary initial/functional data for it to work.

This is the final result.

Search Component

Illustration of the ins and outs of the search component

We will create a search component which fetches its own necessary data (categories, in our case), and can be also preset from outside (selectedCategoryId and searchTerm).

Notice that we receive only the id from outside, meaning that the wrapper component does NOT have to fetch any category on order to pass the selected category — This is especially crucial if we, for example, read the selected category from a URL query parameter.

So let’s begin by creating the Category model and store relevant files:

Great.

Take your time and read through this so you’ll understand every file.
There might be quite a few to create the category store, but this is the beauty of separation of concerns.

Now let’s build the search component (I’m using Angular Material to facilitate the development and reduce code):

Nothing quite special here, but let’s walk over 2 Akita related things:

  1. fetchCategories — This is the method which is responsible for fetching the data that the search component requires in order to show categories in the select box. We make use of the categoryStoreService which in turn will call the categoryApiService to fetch the categories, and then will set them in the store.
  2. It’s important to remember that since we use a store to manage our states, and what categories are available in our application — is a state, we do not assign the returned categories from the categoryStoreService (which passes the values from categoryApiService), but rather use the query concept of Akita to select the categories we’ve inserted into the store.
    We do this by using this.categories$ = this.categoryQuery.selectAll();

Now we should have a search component which takes care of its dependency data, but can be also set from outside.
It’s independent, so we can just take it and use it where ever we need it.

Take a look at a functioning demo.

Pitfall

I’ve a riddle for you:

Imagine we read the selected category from a URL query parameter.
We pass it to the search component when the page just loads.

Will the category selector in the search component be set to the category with the corresponding ID?

Yes/No — Try to explain your answer.

Answer

The answer is no, it won’t work. And here’s why:

When the page loads and we read and pass the category id to the search component — the search component is in the middle of fetching the categories.

In the meantime, it realises that you passed a category id to set the category selector with it, and thus tries to match the id you passed with the id of the categories available in the component.

But, alas — we’re still fetching the data, so we don’t have any categories to match!

So the result is that the selector does not show any category as selected.

Fix

There’s an easy fix for this (though I’m not quite sure if this is a best practice (Netanel Basal mind to share your thoughts on this?):

We convert the getEntity query which is a synchronous operation with selectEntity, which basically does the same — only with an observable.
So you can lock the subscription with the category id you passed to the search component:

this.categoryQuery.selectEntity(changes.selectedCategoryId.currentValue).subscribe((category) => { this.selectedCategory = category });

Now this should work (of course, you’d need to handle unsubscription. Maybe use take(1) since you only want to wait once for the category)

Cheers. 🍻

--

--