Skip to main content

Entity Store Usage

Let’s say we have the following EntityStore:

articles.store.ts
interface Comment {
id: ID;
text: string;
}

interface Article {
id: ID;
comments: Comment[];
title: string;
}

interface ArticlesState extends EntityState<Article, number> {}

@StoreConfig({ name: 'articles' })
class ArticlesStore extends EntityStore<ArticlesState> {}

We have an EntityStore which holds a collection of articles. Each article holds an array of comments. Akita provides helper methods that take care of the grunt work.

API

arrayAdd

import { arrayAdd } from '@datorama/akita';

articlesStore.update(1, ({ comments }) => ({
comments: arrayAdd(comments, newComments)
}));

arrayRemove

import { arrayRemove } from '@datorama/akita';

articlesStore.update(1, ({ comments }) => ({
comments: arrayRemove(comments, ids)
}));

// Remove by predicate
articlesStore.update(1, ({ comments }) => ({
comments: arrayRemove(comments, predicateFn)
}));

arrayUpdate

import { arrayUpdate } from '@datorama/akita';

articlesStore.update(1, ({ comments }) => ({
comments: arrayUpdate(comments, id/s, { text: 'New text' })
}));

// Update by predicate
articlesStore.update(1, ({ comments }) => ({
comments: arrayUpdate(comments, predicateFn, { text: 'New text' })
}));

arrayUpsert

import { arrayUpsert } from '@datorama/akita';

articlesStore.update(1, ({ comments }) => ({
comments: arrayUpsert(comments, id, { text: 'New text' })
}));
tip

The first parameter is typed, so you’ll get intelligent code completion suggesting only keys that are typed as Array.

Each function takes an optional idKey which defaults to id:

articlesStore.update(1, ({ comments }) => ({
comments: arrayUpdate(comments, 3, { text: 'New text' }, 'comment_id')
}));

arrayToggle

Adds a value to an array if it doesn't exist yet or removes it if already present. Objects are compared by identity by default. You can override it by providing a custom compare function.

tip

Akita provides two common comparators for arrayToggle: The byId() and byKey(key: string) compare function.

Toggling an array of objects:

import { arrayToggle } from '@datorama/akita';

articlesStore.update(1, ({ comments }) => ({
comments: arrayToggle(comments, { id: 1, text: 'New text' }, byId())
}));

Toggling an array of primitive values:

import { arrayToggle } from '@datorama/akita';

arrayToggle(['a', 'b'], 'c'); // returns ['a', 'b', 'c']
arrayToggle(['a', 'b', 'c'], 'b'); // returns ['a', 'c']

Store Usage

We can use the same helpers for properties belongs to a Store. For example:

auth.store.ts
import { arrayAdd } from '@datorama/akita';

export interface AuthState {
permissions: string[];
}

@StoreConfig({ name: 'auth' })
export class AuthStore extends Store<AuthState> {
constructor() {
super({ permissions: [] });
}

addPermission(permission: string) {
this.update(({ permissions }) => ({
permissions: arrayAdd(permissions, 'ADMIN')
}));
}
}

Query Helper

That takes care of the CRUD operations, but we also have some good stuff added to the Query; Akita now provides a special operator to query specific items from a collection - arrayFind:

import { arrayFind } from '@datorama/akita';

const selectComment$ = this.articlesQuery
.selectEntity(1, 'comments')
.pipe(arrayFind(commentId))

const selectComments$ = this.articlesQuery
.selectEntity(1, 'comments')
.pipe(arrayFind([id, id, id]))

const selectCommentsByPredicate$ = this.articlesQuery
.selectEntity(1, 'comments')
.pipe(arrayFind(comment => comment.text.includes(..)))

const admins$ = authQuery
.select('permissions')
.pipe(arrayFind(permission => permission === 'ADMIN'));

The added advantage is that these observables will only fire if one of the items in the resulting collection has been modified, via an update, add or delete operation.