State Array Utils

Entity Store

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> {}
@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. For example:

import { arrayUpdate, arrayAdd, arrayRemove, arrayUpsert } from '@datorama/akita';
// add comments
articlesStore.update(1, entity => ({
comments: arrayAdd(entity.comments, comments)
}));
// remove comments
articlesStore.update(1, entity => ({
comments: arrayRemove(entity.comments, ids)
}));
// remove by predicate
articlesStore.update(1, entity => ({
comments: arrayRemove(entity.comments, predicateFn)
}));
// update comment
articlesStore.update(1, entity => ({
comments: arrayUpdate(entity.comments, id/s, { text: 'new text' })
}));
// update by perdicate
articlesStore.update(1, entity => ({
comments: arrayUpdate(entity.comments, predicateFn, { text: 'new text' })
}));
// upsert comment
articlesStore.update(1, entity => ({
comments: arrayUpsert(entity.comments, id, { text: 'new text' })
}));

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

Store

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

export interface CounterModel {
count: number;
names: string[];
}
@StoreConfig({ name: 'store' })
export class CounterStore extends Store<CounterModel> {
constructor() {
super({ count: 0, names: [] });
}
addName(name: string) {
this.update(state => ({
names: arrayAdd(state.names, 'newName')
}));
}
}

Query

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. For example:

import { arrayFind } from '@datorama/akita';
selectComment$ = this.articlesQuery.selectEntity(1, 'comments').pipe(
arrayFind(commentId)
)
selectComments$ = this.articlesQuery.selectEntity(1, 'comments').pipe(
arrayFind([id, id, id])
)
selectCommentsByPredicate$ = this.articlesQuery.selectEntity(1, 'comments').pipe(
arrayFind(comment => comment.text.includes(..))
)
// Or with any other array store property (primitive values)
// names: ['a', 'Netanel', 'c']
counterQuery.select('names').pipe(arrayFind(name => name === 'Netanel'));
// id based objects
// names: [{ id: 1 }, { id: 2 } ]
counterQuery.select('names').pipe(arrayFind(id));

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.