Getting Started with Angular, Akita & Firebase
Tags: akita, angular, firebase
Published: 2019-06-05 15:52:05

---
title: Getting Started with Angular, Akita & Firebase
published: true
description: Getting Started with Akita and Firebase
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2kb7pk7l1z8b38xykvhm.png
tags: akita, angular, firebase
---

So you are a big fan of Firebase, but you also want to start working with Akita (or vise versa). How do the two play together? As it turns out, very well 😁 This is due to the fact that both have a lot in common: They are both observable-based, robust, well-documented solutions in their respective areas.

In this article, I will show an example of managing a bookstore inventory using Akita with AngularFire, the official Angular library for Firebase. 

It assumes that you have some working knowledge of Akita and Firebase. If not, please start with the Akita basics / AngularFire basics.

## Setting Up AngularFire
First, we need to install the <a href="https://github.com/angular/angularfire2">AngularFire</a> library:

```shell
npm install @angular/fire
```

And set our `firebase` settings in the environment file:
```ts
// environment.ts

export const environment = {
  production: false,
  firebase: {
    apiKey: 'yourkey',
    projectId: 'yourid',
  }
};
```

Next, we need to import the `AngularFireModule` into our application and call the `initializeApp` method passing the configuration object we set before.

```ts
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

Now that we have firebase in our application, let's add Akita.

## Setting Up Akita
Adding Akita to our project is easy. We can use the NG add schematic by running the following command:

```shell
ng add @datorama/akita
```

The above command adds Akita, Akita's dev-tools, and Akita's schematics into our project. The next step is to create a store. We need to maintain a collection of books, so we scaffold a new entity feature:

```shell
ng g af books
``` 

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

```ts
// book.model.ts
import { ID } from '@datorama/akita';

export interface Book {
  id: ID;
  title: string;
}

// books.store.ts
export interface BooksState extends EntityState<Book> {}

@Injectable({ providedIn: 'root' })
@StoreConfig({ name: 'books' })
export class BooksStore extends EntityStore<BooksState, Book> {

  constructor() {
    super();
  }

}

// books.query.ts
@Injectable({ providedIn: 'root' })
export class BooksQuery extends QueryEntity<BooksState, Book> {

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

```

The next thing I will do is create a reusable abstraction around the collection <a href="https://github.com/angular/angularfire2/blob/master/docs/firestore/collections.md#statechanges">`stateChanges`</a> API. `stateChanges` returns an observable which emits collection changes as they occur. We can leverage it to  update our stores transparently:

```ts
import { AngularFirestoreCollection } from '@angular/fire/firestore';
import { EntityStore, withTransaction } from '@datorama/akita';

export function syncCollection<T>(collection: AngularFirestoreCollection<T>, store: EntityStore<any, any>) {
  function updateStore(actions) {

    if(actions.length === 0) {
      store.setLoading(false);
      return;
    }

    for ( const action of actions ) {
      const id = action.payload.doc.id;
      const entity = action.payload.doc.data();

      switch( action.type ) {
        case 'added':
          store.add({ id, ...entity });
          break;
        case 'removed':
          store.remove(id);
          break;
        case 'modified':
          store.update(id, entity);
      }
    }
  }

  return collection.stateChanges().pipe(withTransaction(updateStore));
}
```

The `syncCollection` function takes the collection and the store, listens for any state changes in the collection, and update the store based on the emitted action. We also use the <a href="https://netbasal.gitbook.io/akita/general/transactions">`withTransaction`</a> as we want to dispatch one action when we finish with the updates.

Now, we can use it in our books service:

```ts
import { Injectable } from '@angular/core';
import { BooksStore } from './books.store';
import { AngularFirestore } from '@angular/fire/firestore';
import { syncCollection } from '../syncCollection';

@Injectable({ providedIn: 'root' })
export class BooksService {
  private collection = this.db.collection('books');

  constructor(private booksStore: BooksStore, private db: AngularFirestore) {
  }

  connect() {
    return syncCollection(this.collection, this.booksStore);
  }

  addBook(title: string) {
    this.collection.add({ title });
  }

  removeBook(id: string) {
    this.collection.doc(id).delete();
  }

  editBook(id: string) {
    this.collection.doc(id).update({ title: Math.random().toFixed(2).toString() });
  }
}

```

We use the firebase API to create methods for add, edit, and remove books. Let's use them in our books component:

```ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { BooksQuery } from './state/books.query';
import { BooksService } from './state/books.service';
import { untilDestroyed } from 'ngx-take-until-destroy';

@Component({
  selector: 'app-books'
})
export class AppComponent implements OnInit, OnDestroy {
  books$ = this.booksQuery.selectAll();
  loading$ = this.booksQuery.selectLoading();

  constructor(private booksQuery: BooksQuery, private booksService: BooksService) {
  }

  ngOnInit() {
    this.booksService.connect().pipe(untilDestroyed(this)).subscribe();
  }

  addBook(input: HTMLInputElement) {
    this.booksService.addBook(input.value);
    input.value = '';
  }

  removeBook(id: string) {
    this.booksService.removeBook(id);
  }

  editBook(id: string) {
    this.booksService.editBook(id);
  }

  trackByFn(i, book) {
    return book.id;
  }

  ngOnDestroy() {
  }
}

```

And here is the component's template:
```html
<ng-container *ngIf="loading$ | async; else books">
  <h1>Loading...</h1>
</ng-container>

<ng-template #books>
  <input placeholder="Add Book..." #input (keyup.enter)="addBook(input)">

  <ul>
    <li *ngFor="let book of books$ | async; trackBy: trackByFn">
      {{ book.title }}
      <button (click)="editBook(book.id)">Edit</button>
      <button (click)="removeBook(book.id)">Delete</button>
    </li>
  </ul>
</ng-template>

```

And that's all. The only thing we need to do is to update the firebase collection as we usually do, and let the `syncCollection` functionality take care of everything.

Let's see the result:

![](https://thepracticaldev.s3.amazonaws.com/i/qkpwnwb2fg2gp7pdhh5t.gif)