Introduction
This guide is also available on youtube here.
The Firefly Semantics Slice Entity Store ( EStore<E>
) manages an observable array of entity instances.
Use it in single page, web, mobile, and server side applications for reactive state management.
Each EStore<E>
instance also has utility Observable
s used to indicate whether the store is loading
or if a search associated with the store is being conducted as indicated by the searching
property.
In this guide we will use EStore<E>
instances to store Todo
entities and show applications of the API that EStore<E>
supports.
Will will also add reactive Slice
instances that track which Todo
entities are complete and which are incomplete.
Stackblitz
Open a new Stackblitz Typescript project and add @fireflysemantics/slice
to the dependencies.
Notice the peer dependencies that are automatically pulled in.
This is a completed version. Feel free to fork it or add the snippets as we are going along.
Model
We will be storing Todo entities. This is the model:
interface Todo {
id?: string;
gid?: string;
complete: boolean;
title: string;
}
We are using Typescript interfaces.
Feel free to switch the interface out with classes, types, etc.
Entities
We will use these Todo entity instances for demoing:
const TODO_ID_1 = "1";
const TODO_ID_2 = "2";
const TODO1: Todo = {
id: TODO_ID_1,
complete: false,
title: "You complete me!"
};
const TODO2: Todo = {
id: TODO_ID_2,
complete: true,
title: "You completed me!"
};
let todos: Todo[] = [TODO1, TODO2];
ID Assignment
Our Todo
interface has two ids. The gid
(Global ID) is assigned by Slice, such that the store can always identify an entity once it is added.
The id
property can be assigned by the server (It is not managed by Slice). In this case we will initialize it while creating the sample data.
Generally speaking this type of approach supports Optimistic User Interfaces, where entities have an ID that allows both the client and the server to identify the entity consistently.
For example consider a chat message. Before it is sent, it will have a unique ID generated on the client. Once it hits the server the server can also assign a unique ID to it and now it will be consistent on both ends.
Constructor Initialization
let store: EStore<Todo> = new EStore<Todo>(todos);
Each entity will have a global ID ( gid
) assigned to it, unless the property is already initialized.
Observing Store Entities
The store can be observed with the obs
property bound to the store
instance.
store.obs.subscribe(todos => {
console.log(todos)
})
The above snippet will log the initial todo
entities that the store is initialized with. Whenever we perform operations (post
, put
, delete
) on the store
the obs
will fire notifying the subscriber function.
We can also perform a sort the observed entities by passing in a sort function to observe
.
In the below case the sort function sorts by title:
Sorting Observed Entities
let todos$ = store11.observe((a, b)=>(a.title > b.title ? -1 : 1));
Retrieve Entities from the Store by ID
const todo1 = store.findOneByID(TODO_ID_1);
const todo2 = store.findOneByID(TODO_ID_2);
Retrieve Store Entities by Predicate
Select all Todo instances that have a title
length greater than 10:
let predicateTodos:Todo[]=store.select(todo=>todo.title.length>10);
Retrieve a Snapshot of All Entities
To retrieve a Todo[]
array snapshot of all the entities
in the store call allSnapshot()
:
const snapshot:Todo[] = store.allSnapshot();
Check Entity Equality by Global ID
Since the EStore
assigns the gid
for us, we can use it to compare entities for equality:
let todo1EqualsTodo1:boolean = store.equalsByGUID(todo1, todo1);
Check Entity Equality by ID
We assigned a unique id
to the entities, thus we can use it to check for equality:
let todo1EqualsTodo1 = store.equalsByID(todo1, todo1);
Add a Single Entity
Use post
to post a single entity.
let store2: EStore<Todo> = new EStore<Todo>();
store2.post(todoPost);
Add an Array of Entities to the Store
Use postA
to post an array of entities.
store2.postA(todos);
Add Multiple Individual Entities to the Store
Use postN
to post multiple individual entities.
store2.postN(TODO1, TODO2);
Update a Single Store Entity
let store5 = new EStore<Todo>();
store5.post(TODO1);
let T1 = store5.findOneByID(TODO1.id);
T1.title = "This Todo has been Updated";
store5.put(T1);
Update an Array of Store Entities
let store6 = new EStore<Todo>(todos);
T1 = store6.findOneByID(TODO1.id);
T1.title = "This Todo has been Updated";
let T2 = store6.findOneByID(TODO2.id);
T2.title = "This Todo has also been Updated";
store5.putA([T1, T2]);
Update Multiple Individual Entities
let store7 = new EStore<Todo>(todos);
T1 = store7.findOneByID(TODO1.id);
T1.title = "This Todo has been Updated";
T2 = store7.findOneByID(TODO2.id);
T2.title = "This Todo has also been Updated";
store5.putN(T1, T2);
Delete a Single Entity
let store8 = new EStore<Todo>();
store8.post(TODO1);
T1 = store5.findOneByID(TODO1.id);
store5.delete(T1);
Delete an Array of Entities
let store9 = new EStore<Todo>(todos);
T1 = store9.findOneByID(TODO1.id);
T2 = store6.findOneByID(TODO2.id);
store5.deleteA([T1, T2]);
Delete Multiple Individual Entities
let store10 = new EStore<Todo>(todos);
T1 = store10.findOneByID(TODO1.id);
T2 = store10.findOneByID(TODO2.id);
store10.deleteN(T1, T2);
Indicate that the Store is Loading
store.loading = true
// When done loading
store.loading = false
// Observe Store Loading
const loading$ = store.observeLoading()
loading$.subscribe(
loading => {
console.log(loading);
});
Indicate that the Store is Searching
store.searching = true;
// When done searching
store.searching = false
// Observe Store Searching
const searching$ = store.observeSearching()
searching$.subscribe(
loading => {
console.log(loading);
});
Set the Store Query
Setting the store.query
property will trigger
notification to any observers of this property.
store.query = "Slice is Awesome!"
Observe the Store Query
const query$ = store.observeQuery()
Indicate That the Store is Being Searched
store.searching = true
// When done loading
store.searching = false
Add / Remove Active Entities
store.addActive(todo1)
store.deleteActive(todo1)
Add an Entity Via Toggle
If todo1
is not in the store the toggle
call will add it.
store.toggle(todo1)
Remove an Entity Via Toggle
If todo1
is in the store the toggle
call will remove it.
store.toggle(todo1)
Check Whether the Store is Empty
store.isEmptySnapshot()
Observe Whether the Store is Empty
store.isEmpty().subscribe(empty => {
console.log(empty)
});
Reset the Store
store.reset()
Check Store Entity Count
store.countSnapshot()
Observe Store Entity Count
store.count().subscribe( c => {
console.log(c)
});
Contains Entity
To check whether the store contains entity
call contains
:
let containsEntity:boolean = store.contains(entity)
Adding Complete and Incomplete Slices
Slices are live observable filters.
We will add two slices for complete
and incomplete
todos.
export const enum TodoSlices {
COMPLETE = "Complete",
INCOMPLETE = "Incomplete"
}
let store12 = new EStore<Todo>(todos);
T1 = store12.findOneByID(TODO1.id);
T2 = store12.findOneByID(TODO2.id);
store12.addSlice(todo => todo.complete, TodoSlices.COMPLETE);
store12.addSlice(todo => !todo.complete, TodoSlices.INCOMPLETE);
const completeTodos = store12.getSlice(TodoSlices.COMPLETE).allSnapshot();
const incompletedTodos = store12.getSlice(TodoSlices.INCOMPLETE).allSnapshot();
const completeTodos$ = store12.getSlice(TodoSlices.COMPLETE).observe();
completeTodos$.subscribe(todos => {
console.log("COMPLETED TODOS");
console.log(JSON.stringify(todos));
});
store12.post({
id: "3",
title: "Adding a Complete Todo",
complete: true
});
When we post the Todo instance with id
3
the completeTodos$ observable fires,
and the Todo is logged by the subscriber of the completeTodos$
.