This is the second part of the introduction to Akita (the first part can be found here). In this part, we’ll create from scratch a working shopping cart by following the principles we learned in the previous article in order to connect the dots.

Live Demo

The Products

First, we need to display a list of products that we’ll get from the backend API:

You already should be familiar with the above code. In Akita, everything starts with defining the entity type, a store, and a query.

Note that in this case, we aren’t creating a factory function, because the state is read-only, meaning we don’t need to create new products and we don’t need to modify the object coming from the server.

Akita recommends that asynchronous logic and update calls should be encapsulated in services, so let’s continue by creating a service which is responsible for fetching the products from the server:

Let’s stop for a second to explain what’s going on in the service.

When using entities store, it’s initial state is pristine , and when you call store.set() , Akita changes it to false. Calling remove() sets it to true again.

This can be used to determine whether the data is present in the store, to save on additional server requests.

Returning to our example, we only initialize a server request if the store’s state is pristine, otherwise returning an observable that next() once and complete.

Now, let’s move on to the components.

Akita encourages the model of smart and dumb (aka stateless and stateful ) components’ architecture. This combination allows us to use the async pipe, combined with the OnPush change strategy to gain better performance.

Presentational Components

Presentational components describe how things look. Typically, they will receive data via inputs() and will communicate via outputs(), aka events.

Let’s create a product component in charge of the presentation of a product:

product.component.ts

The product component receives the product as input() and communicates with its parent via outputs.

Container Components

Container components are concerned with how things work. They provide the data and behavior to presentational or other container components.

Let’s create a products component in charge of displaying a list of products filtered by a search term:

We start by calling the service we created earlier, to fetch the products from the server and update the store.

The selectLoading() is a query method from Akita that provides the value of the loading key from the store reactively.

The initial value of the loading state is set to true and is switched to false when you call store.set() (you can always change it by using the API).

We want to filter the products based on the search value, so we’re listening to the control input, leveraging Akita’s filterBy feature to return products matching the search term.

Tip: We recommend placing the logic for underlying queries inside the query class so it can be more readable and reusable:

cart.query.ts

That’s it for the products page, let’s move on to create the cart.

The Cart

As always, we start by creating Akita’s building blocks:

cart-akita

The observant among you may have noticed two new code samples.

First, we have a factory function that knows how to create new cart items.

Second, there’s the idKey attribute in the CartStore constructor. By default, Akita takes the id key from the entity id field, but in our case, we’re telling Akita — the id key is productId .

Add a Product to the Cart

When a user clicks the add button, we want to add a new cart item to the store. Here is how we can do this:

cart.service.ts

Using the product id, we check that the product doesn’t exist, and add it; Otherwise, we update the quantity.

Here is the code for updating a cart item:

cart.query.ts

We are using the update() method from Akita, passing the productId and a callback function that returns a new immutable updated item.

For brevity, I will skip the parts dealing with subtracting/removing an item from the cart. Let’s move on to the exciting part, the cart list.

Displaying the Cart Items

We need to show the list of cart items and the total amount, but we also need some information from the product, like the title and the price. Therefore we need to join the cartStore with the productsStore.

In Akita, Queries can talk to other queries, join entities from different stores, etc. Here is how we join them:

We’re using the combineLatest() observable to get both the list of cart items and the products. Then we are mapping over them, merging a cart item with the corresponding product based on the productId .

We also want to calculate the cart total without executing the mapping function again, so we’re leveraging one of the share() operators from Rx.

Now, we can display the cart items:

cart.component.ts

You can find the live example here.

Summary

We’ve seen here how the various core concepts of Akita work together to give us an easy to manage shopping cart. This is only a small taste of Akita; It has many more additional features, such as support for active state, transactions, web workers, etc.

I encourage you to explore the API by reading the docs and the source code of the demo application which contains a todos page, a shopping cart, and login management.