Entities
This feature enables the store to act as an entities store. You can think of an entities state as a table in a database,
where each table represents a flat collection of similar entities. Elf's entities state simplifies the process, giving you
everything you need to manage it.
First, you need to install the package by using the CLI command elf-cli install and selecting the entities package,
or via npm:
npm i @ngneat/elf-entities
To use this feature, provide the withEntities props factory function in the createStore call:
import { createStore } from '@ngneat/elf';
import { withEntities } from '@ngneat/elf-entities';
interface Todo {
id: number;
label: string;
}
const todosStore = createStore({ name: 'todos' }, withEntities<Todo>());
This will allow you to use the following ready-made mutations and queries:
Queries
selectAllEntities
Select the entire store's entity collection:
import { selectAllEntities } from '@ngneat/elf-entities';
const todos$ = todosStore.pipe(selectAllEntities());
selectAllEntitiesApply
Select the entire store's entity collection, and apply a filter/map:
import { selectAllEntitiesApply } from '@ngneat/elf-entities';
const titles$ = todosStore.pipe(
selectAllEntitiesApply({
mapEntity: (e) => e.title,
filterEntity: (e) => e.completed,
})
);
In the above example, it'll first apply the filter and then the map function.
getAllEntitiesApply
Get the entire store's entity collection, and apply a filter/map:
import { getAllEntitiesApply } from '@ngneat/elf-entities';
const titles = todosStore.query(
getAllEntitiesApply({
mapEntity: (e) => e.title,
filterEntity: (e) => e.completed,
})
);
selectEntities
Select the entire store's entity collection as object:
import { selectEntities } from '@ngneat/elf-entities';
const todos$ = todosStore.pipe(selectEntities());
selectEntity
Select an entity or a slice of an entity:
import { selectEntity } from '@ngneat/elf-entities';
const todo$ = todosStore.pipe(selectEntity(id));
const title$ = todosStore.pipe(selectEntity(id, { pluck: 'title' }));
const title$ = todosStore.pipe(selectEntity(id, { pluck: (e) => e.title }));
selectEntityByPredicate
Select an entity from the store by predicate:
import { selectEntityByPredicate } from '@ngneat/elf-entities';
const todo$ = todosStore.pipe(
selectEntityByPredicate(({ completed }) => !completed)
);
const title$ = todosStore.pipe(
selectEntityByPredicate(({ completed }) => !completed, {
pluck: 'title',
idKey: '_id',
})
);
const title$ = todosStore.pipe(
selectEntityByPredicate(({ completed }) => !completed, {
pluck: (e) => e.title,
idKey: '_id',
})
);
selectMany
Select multiple entities from the store:
import { selectMany } from '@ngneat/elf-entities';
const todos$ = todosStore.pipe(selectMany([id, id]));
const titles$ = todosStore.pipe(selectMany(id, { pluck: 'title' }));
const titles$ = todosStore.pipe(selectMany(id, { pluck: (e) => e.title }));
selectManyByPredicate
Select multiple entities from the store by predicate:
import { selectManyByPredicate } from '@ngneat/elf-entities';
const todos$ = todosStore.pipe(
selectManyByPredicate((entity) => entity.completed === false)
);
const titles$ = todosStore.pipe(
selectManyByPredicate((entity) => entity.completed === false, {
pluck: 'title',
})
);
const titles$ = todosStore.pipe(
selectManyByPredicate((entity) => entity.completed === false, {
pluck: (e) => e.title,
})
);
selectFirst
Select the first entity from the store:
import { selectFirst } from '@ngneat/elf-entities';
const first$ = todosStore.pipe(selectFirst());
selectLast
Select the last entity from the store:
import { selectLast } from '@ngneat/elf-entities';
const last$ = todosStore.pipe(selectLast());
selectEntitiesCount
Select the store's entity collection size:
import { selectEntitiesCount } from '@ngneat/elf-entities';
const count$ = todosStore.pipe(selectEntitiesCount());
selectEntitiesCountByPredicate
Select the store's entity collection size:
import { selectEntitiesCountByPredicate } from '@ngneat/elf-entities';
const count$ = todosStore.pipe(
selectEntitiesCountByPredicate((entity) => entity.completed)
);
getAllEntities
Get the entity collection:
import { getAllEntities } from '@ngneat/elf-entities';
const todos = todosStore.query(getAllEntities());
getEntitiesIds
Get the entities ids:
import { getEntitiesIds } from '@ngneat/elf-entities';
const todosIds = todosStore.query(getEntitiesIds());
getEntity
Get an entity by id:
import { getEntity } from '@ngneat/elf-entities';
const todo = todosStore.query(getEntity(id));
getEntityByPredicate
Get first entity from the store by predicate:
import { getEntityByPredicate } from '@ngneat/elf-entities';
const todo = todosStore.query(
getEntityByPredicate(({ title }) => title === 'Elf')
);
hasEntity
Returns whether an entity exists:
import { hasEntity } from '@ngneat/elf-entities';
if (todosStore.query(hasEntity(id))) {
}
getEntitiesCount
Get the store's entity collection size:
import { getEntitiesCount } from '@ngneat/elf-entities';
const count = todosStore.query(getEntitiesCount());
getEntitiesCountByPredicate
Get the store's entity collection size:
import { getEntitiesCountByPredicate } from '@ngneat/elf-entities';
const count = todosStore.query(
getEntitiesCountByPredicate((entity) => entity.completed)
);
Mutations
setEntities
Replace current collection with the provided collection:
import { setEntities } from '@ngneat/elf-entities';
todosStore.update(setEntities([todo, todo]));
setEntitiesMap
Replace current collection with the provided map:
import { setEntitiesMap } from '@ngneat/elf-entities';
const todos = {
1: {
id: 1,
task: 'Buy milk',
},
2: {
id: 2,
task: 'Fix car',
},
};
todosStore.update(setEntitiesMap(todos));
addEntities
Add an entity or entities to the store:
import { addEntities } from '@ngneat/elf-entities';
todosStore.update(addEntities(todo));
todosStore.update(addEntities([todo, todo]));
todosStore.update(addEntities([todo, todo], { prepend: true }));
addEntitiesFifo
Add an entity or entities to the store using fifo strategy:
import { addEntitiesFifo } from '@ngneat/elf-entities';
todosStore.update(addEntitiesFifo([entity, entity]), { limit: 3 });
updateEntities
Update an entity or entities in the store:
import { updateEntities } from '@ngneat/elf-entities';
todosStore.update(updateEntities(id, { name }));
todosStore.update(updateEntities(id, (entity) => ({ ...entity, name })));
todosStore.update(updateEntities([id, id, id], { open: true }));
updateEntitiesByPredicate
Update an entity or entities in the store:
import { updateEntitiesByPredicate } from '@ngneat/elf-entities';
todosStore.update(
updateEntitiesByPredicate(({ count }) => count === 0, { open: false })
);
todosStore.update(
updateEntitiesByPredicate(({ count }) => count === 0),
(entity) => ({ ...entity, open: false })
);
updateAllEntities
Update all entities in the store:
import { updateAllEntities } from '@ngneat/elf-entities';
todosStore.update(updateAllEntities({ name: 'elf' }));
todosStore.update(
updateAllEntities((entity) => ({ ...entity, count: entity.count + 1 }))
);
upsertEntities
Add or update entities.
To identify entities in the store, every entity must have an id property. Any partial entities will be merged with the existing ones:
import { upsertEntitiesBy } from '@ngneat/elf-entities';
todosStore.update(upsertEntities({ id: '1', happy: true }));
todosStore.update(
upsertEntities([
{ id: '1', happy: true },
{ id: '2', name: 'elf 2', happy: false },
])
);
upsertEntitiesById
Insert or update an entity. When the id isn't found, it creates a new entity; otherwise, it performs an update:
import { upsertEntitiesById } from '@ngneat/elf-entities';
const creator = (id) => createTodo(id);
todosStore.update(
upsertEntitiesById(1, {
updater: { name: 'elf' },
creator,
})
);
todosStore.update(
upsertEntitiesById([1, 2], {
updater: (entity) => ({ ...entity, count: entity.count + 1 }),
creator,
})
);
To perform a merge between a new entity and an updater result, use the mergeUpdaterWithCreator option:
todosStore.update(
upsertEntitiesById([1, 2], {
updater: (entity) => ({ ...entity, name }),
creator,
mergeUpdaterWithCreator: true,
})
);
The above example will first create the entity using the creator method, then pass the result to the updater method, and merge both.
updateEntitiesIds
Update id of an entity or entities in the store:
import { updateEntitiesIds } from '@ngneat/elf-entities';
todosStore.update(updateEntitiesIds(oldId, newId));
todosStore.update(updateEntitiesIds([oldId1, oldId2], [newId1, newId2]));
The most common use case for this is "optimistic updates":
function addTodo(todo: Todo) {
const tempId = generateRandomId();
todosStore.update(addEntities({ ...todo, id: tempId }));
addTodoToServer(todo).then(
(response) => {
todosStore.update(
updateEntitiesIds(tempId, response.id),
updateEntities(response.id, response)
);
},
(error) => {
todosStore.update(deleteEntities(tempId));
// handle error
}
);
}
deleteEntities
Delete an entity or entities from the store:
import { deleteEntities } from '@ngneat/elf-entities';
todosStore.update(deleteEntities(id));
todosStore.update(deleteEntities([id, id]));
deleteEntitiesByPredicate
Delete an entity or entities from the store:
import { deleteEntitiesByPredicate } from '@ngneat/elf-entities';
todosStore.update(deleteEntitiesByPredicate(({ completed }) => completed));
deleteAllEntities
Delete all entities from the store:
import { deleteAllEntities } from '@ngneat/elf-entities';
todosStore.update(deleteAllEntities());
moveEntity
Moves an entity within the store:
import { moveEntity } from '@ngneat/elf-entities';
todosStore.update(moveEntity({ fromIndex: 0, toIndex: 1 }));
idKey
By default, Elf takes the id key from the entity id field. To change it, you can pass the idKey option to
the withEntities function:
import { createStore } from '@ngneat/elf';
import { addEntities } from '@ngneat/elf-entities';
interface Todo {
_id: number;
label: string;
}
const todosStore = createStore(
{ name: 'todos' },
withEntities<Todo, '_id'>({ idKey: '_id' })
);
initialValue
In case that you need to start the entities state with a value, you can specify it in the initialValue configuration:
import { createStore } from '@ngneat/elf';
const store = createStore(
{ name },
withEntities<Widget>({ initialValue: [{ id: 1, name: '' }] })
);