Entity Store Usage
Let’s say we have the following EntityStore
:
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' })
}));
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.
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:
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.