Build a Real-Time Shopping List App with Angular, Socket.IO & Akita
Tags: angular, akita, socketio
Published: 2019-06-03 14:04:44

---
title: Build a Real-Time Shopping List App with Angular, Socket.IO & Akita
published: true
description: Create a Shopping List Using Angular, Akita, and Socket.io
tags: Angular, Akita, Socket.io
cover_image: https://thepracticaldev.s3.amazonaws.com/i/a1clif4wh7cc198uueli.png
---

In this article, we are going to build a real-time shopping list using Angular, Akita, and Socket.io. Our example application will feature three things: Adding a new item, deleting an item, and change the item's complete status.

## What is Akita?
<a href="https://netbasal.gitbook.io/akita/">Akita</a> is a state management pattern, built on top of RxJS, which takes the idea of multiple data stores from Flux and the immutable updates from Redux, along with the concept of streaming data, to create the Observable Data Store model.

Akita encourages simplicity. It saves you the hassle of creating boilerplate code and offers powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.

## Create the Server
We will start by creating the server. First, we install the express application generator:
```shell
npm install express-generator -g
```

Next, we create a new express application:
```shell
express --no-view shopping-list
```


Now, delete everything in your `app.js` file and replace it with the following code:

```javascript
const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);

let list = [];

io.on('connection', function(socket) {

  // Send the entire list
  socket.emit('list', {
    type: 'SET',
    data: list
  });

  // Add the item and send it to everyone
  socket.on('list:add', item => {
    list.push(item);
    io.sockets.emit('list', {
      type: 'ADD',
      data: item
    });
  });

  // Remove the item and send the id to everyone
  socket.on('list:remove', id => {
    list = list.filter(item => item.id !== id);

    io.sockets.emit('list', {
      type: 'REMOVE',
      ids : id
    });
  });

  // Toggle the item and send it to everyone
  socket.on('list:toggle', id => {
    list = list.map(item => {
      if( item.id === id ) {
        return {
          ...item,
          completed: !item.completed
        }
      }
      return item;
    });

    io.sockets.emit('list', {
      type: 'UPDATE',
      ids : id,
      data: list.find(current => current.id === id)
    });
  })
});

server.listen(8000);

module.exports = app;


```  

We define a new `socket-io` server and save the user's list in memory (in real-life it will be saved in a database). We create several listeners based on the actions we need in the client: SET (GET), ADD, REMOVE, UPDATE. 

Note that we use a specific pattern. We send the action `type` and the action `payload`. We will see in a second how we use this with Akita.

## Create the Angular Application

First, we need to install the `angular-cli` package and create a new Angular project:

```shell
npm i -g @angular/cli
ng new akita-shopping-list
```

Next, we need to add Akita to our project:
```shell
ng add @datorama/akita
```

The above command automatically adds Akita, Akita's dev-tools, and Akita's schematics into our project. We need to maintain a collection of items, so we scaffold a new entity feature:

```shell
ng g af shopping-list
``` 

This command generates a store, a query, a service, and a model for us:

```typescript
// store
import { Injectable } from '@angular/core';
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
import { ShoppingListItem } from './shopping-list.model';

export interface ShoppingListState extends EntityState<ShoppingListItem> {}

@Injectable({ providedIn: 'root' })
@StoreConfig({ name: 'shopping-list' })
export class ShoppingListStore extends EntityStore<ShoppingListState, ShoppingListItem> {

  constructor() {
    super();
  }

}


// query
import { Injectable } from '@angular/core';
import { QueryEntity } from '@datorama/akita';
import { ShoppingListStore, ShoppingListState } from './shopping-list.store';
import { ShoppingListItem } from './shopping-list.model';

@Injectable({ providedIn: 'root' })
export class ShoppingListQuery extends QueryEntity<ShoppingListState, ShoppingListItem> {

  constructor(protected store: ShoppingListStore) {
    super(store);
  }

}

// model
import { guid, ID } from '@datorama/akita';

export interface ShoppingListItem {
  id: ID;
  title: string;
  completed: boolean;
}

export function createShoppingListItem({ title }: Partial<ShoppingListItem>) {
  return {
    id: guid(),
    title,
    completed: false,
  } as ShoppingListItem;
}

```

Now, let's install the `socket-io-client` library:

```shell
npm i socket.io-client
```

and use it in our service:

```typescript
import { Injectable } from '@angular/core';
import io from 'socket.io-client';
import { ShoppingListStore } from './state/shopping-list.store';
import { ID, runStoreAction, StoreActions } from '@datorama/akita';
import { createShoppingListItem } from './state/shopping-list.model';

const resolveAction = {
  ADD: StoreActions.AddEntities,
  REMOVE: StoreActions.RemoveEntities,
  SET: StoreActions.SetEntities,
  UPDATE: StoreActions.UpdateEntities
};

@Injectable({ providedIn: 'root' })
export class ShoppingListService {
  private socket;

  constructor(private store: ShoppingListStore) {
  }

  connect() {
    this.socket = io.connect('http://localhost:8000');

    this.socket.on('list', event => {
      runStoreAction(this.store.storeName, resolveAction[event.type], {
        payload: {
          entityIds: event.ids,
          data: event.data
        }
      });
    });

    return () => this.socket.disconnect();
  }

  add(title: string) {
    this.socket.emit('list:add', createShoppingListItem({ title }));
  }

  remove(id: ID) {
    this.socket.emit('list:remove', id);
  }

  toggleCompleted(id: ID) {
    this.socket.emit('list:toggle', id);
  }
}

```

First, we create a `connect` method where we connect to our socket server and listening for the `list` event. When this event fires, we call the `runStoreAction` method, passing the store name, the action, the entities id, and the data we get from the server. We also return a dispose function so we won't have a memory leak.

Next, We create three methods, `add`, `remove` and `toggleCompleted` that emit the corresponding events with the required data. Now, we can use it in our component:


```typescript
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ShoppingListService } from './shopping-list.service';
import { Observable } from 'rxjs';
import { ShoppingListQuery } from './state/shopping-list.query';
import { ID } from '@datorama/akita';
import { ShoppingListItem } from './state/shopping-list.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
  items$: Observable<ShoppingListItem[]>;
  private disposeConnection: VoidFunction;

  constructor(private shoppingListService: ShoppingListService, 
              private shoppingListQuery: ShoppingListQuery) {
  }

  ngOnInit() {
    this.items$ = this.shoppingListQuery.selectAll();
    this.disposeConnection = this.shoppingList.connect();
  }

  add(input: HTMLInputElement) {
    this.shoppingListService.add(input.value);
    input.value = '';
  }

  remove(id: ID) {
    this.shoppingListService.remove(id);
  }

  toggle(id: ID) {
    this.shoppingListService.toggleCompleted(id);
  }

  track(_, item) {
    return item.title;
  }

  ngOnDestroy() {
    this.disposeConnection();
  }

}

```

And the component's HTML:

```html

<div>

  <div>
    <input(keyup.enter)="add(input)" #input placeholder="Add Item..">
  </div>

  <ul>
    <li *ngFor="let item of items$ | async; trackBy: track">
      <div [class.done]="item.completed">{{item.title}}
        <i (click)="remove(item.id)">X</i>
        <i (click)="toggle(item.id)">done</i>
      </div>
    </li>
  </ul>

</div>

```

And here is the result:
![](https://thepracticaldev.s3.amazonaws.com/i/ud8dschg65rlinhn39nf.gif)


That is pretty cool. Here is a <a href="https://github.com/ArielGueta/akita-socketio">link</a> to the complete code.