Introduction to the Firefly Semantics Slice Reactive Entity Store
Updated Version
There is an updated version of this guide here:
The Firefly Semantics Slice Entity Store ( EStore<E>
) contains an observable array of entity instances.
Use it in single page, web, mobile, and server side applications for reactive state management.
In this guide we will use it 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
can be assigned by the server. 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 for 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.
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
const snapshot = 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
Since the EStore
assigns the gid
for us, we can use it to compare entities 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);
Observe the Entities in the Store
Whenever we add, update, or delete entities from the store the subscription will fire notifying the subscription function of the update to the array of Todo
entities.
Below we simply log the new array contents:
let store11: EStore<Todo> = new EStore<Todo>(todos);store11.observable.subscribe(todos => {console.log("THE REACTIVE TODO ARRAY IS: \n");
console.log(JSON.stringify(todos));
});T1 = store11.findOneByID(TODO1.id);
T2 = store11.findOneByID(TODO2.id);
store11.post({ id: "3", title: "Adding another Todo Entity", complete: false });
store11.delete(T1);
T2.title = "This Todo has also been Updated";
store11.put(T2);
We can also perform a sort the observed entities. In the below case the sort function sorts by title:
let todos$ = store11.observe((a, b)=>(a.title > b.title ? -1 : 1));
Indicate that the Store is Loading
store.loading = true
// When done loading
store.loading = false
Observe Store Loading Indicator
const loading$ = store.observeLoading()
loading$.subscribe(
loading => {
console.log(loading);
});
Set the Store Query
store.query = "Slice is Awesome!"
Setting the store.query
property will trigger notification to any observers of this property.
Observe the Store Query
const query$ = store.observeQuery()
Indicate That the Store is Being Searched
store.searching = true
// When done loading
store.searching = false
Observe Store Searching Indicator
const searching$ = store.observeSearching()
searching$.subscribe(
searching => {
console.log(searching);
});
Indicate That an Entity is Active
store.addActive(todo1)
Remove an Active Entity
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)
});
Clear the Store
store.clear()
Check Store Entity Count
store.countSnapshot()
Observe Store Entity Count
store.count().subscribe( c => {
console.log(c)
});
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$
.