ngrx-store-idb
Simple syncing between your ngrx store and IndexedDB. This library was adapted from excellent ngrx-store-localstorage library.
Description
This library is intended for projects implemented with Angular and NGRX. It saves (selected parts of) your store into IndexedDB and again reads them back upon your application load. This is acomplished by installing NGRX metareducer.
The main difference to ngrx-store-localstorage library is that this library uses IndexedDB for storage.
Local storage can store only 5MB of data, can store only strings and is synchronous.
IndexedDB uses Structured Cloning Algorithm to serialize the data, is asynchronous and has much higher storage limit.
The data from storage is first read and merged into NGRX store immediatelly after NGRX store is initialized and NGRX effects are activated. There are additional merges executed when a new store feature is activated (e.g. for lazy loaded module).
Concurrency issues
Since your users can open your web application in multiple tabs or windows it can present interesting challenges to design of your application. If there are multiple instances of your application open at the same time, they fight for access to IndexedDB and will overwrite the data. This might not be a problem if you design for it but usually it will be a problem.
The library supports measures to handle concurrency issues. Each application (running this library) instance will try to acquire a so called lock over IndexedDB store and then will periodically update it to signal that it is still using it. If another instance opens, it will discover that it can not acquire the lock and will either stop syncing state to IndexedDB (but will still be able to rehydrate state) or will fail to load. See concurrency
options.
If your application needs to synchronise state across multiple tabs/windows then you are better off using ngrx-store-localstorage library. Local storage supports event notification when the state changes allowing you to react to changes easily.
Dependencies
ngrx-store-idb
depends on
@ngrx/store,
Angular 2+,
idb-keyval
Usage
npm install ngrx-store-idb --save
- Import NgrxStoreIdbModule in your main AppModule.
- Profit!
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgrxStoreIdbModule } from 'ngrx-store-idb';
@NgModule({
imports: [
BrowserModule,
NgrxStoreIdbModule.forRoot()
]
})
export class MyAppModule {}
API
NgrxStoreIdbModule.forRoot(options: NgrxStoreIdbOptions)
Install module that bootstraps NGRX metareducer and registers additional NGRX effects and Angular service.
Arguments
-
options
An object that matches with theNgrxStoreIdbOptions
interface. Uses default values when not provided.
NgrxStoreIdbOptions
An interface defining the configuration attributes to bootstrap NGRX metareducer. The following are properties which compose NgrxStoreIdbOptions
:
-
keys
State keys/slices to sync with IndexedDB. The keys can be defined in two different formats:-
string[]
: Array of strings representing the state (reducer) keys. Full state will be synced (e.g.NgrxStoreIdbModule.forRoot({keys: ['todos']})
). -
object[]
: Array of objects where for each object the key represents the state slice and the value represents properties of given slice which should be synced. This allows for the partial state sync (e.g.NgrxStoreIdbModule.forRoot({keys: [{todos: ['name', 'status'] }, ... ]})
). Default value is null (i.e. not used). Can not be used together withunmarshaller/marshaller
options.
-
-
rehydrate: boolean
: Pull initial state from IndexedDB on startup, this will default totrue
. -
saveOnChange: boolean
: Iftrue
then state will be synced to IndexedDB only if it differs from previous synced value. It uses object equality to compare previous and current state. See\projects\ngrx-store-idb\src\lib\ngrx-store-idb.metareducer.ts#statesAreEqual()
for details of the comparison algorithm. Default value istrue
. -
syncCondition: (state: any, action: Action) => boolean
: Custom comparator used to determine if the current state should be synced to IndexedDB. You can use it to e.g. implement your own state comparison or trigger synchronisation only for certain actions. Default isnull
. -
unmarshaller: (state: any, rehydratedState: any) => any
: Defines the reducer to use to merge the rehydrated state from storage with the state from the ngrx store. Must be defined together withmarshaller
. Can not be used together withkeys
. If unspecified, defaults to performing a full deepmerge. -
marshaller: (state: any) => any
: Method used to marshall store state to be written into IndexedDB. Must be used together withunmarshaller
. Can not be used together withkeys
. Default marshaller saves to whole state. -
debugInfo: boolean
: Set to true to see debug messages in console. It can help you to understand when and how is state synced. Default istrue
. -
concurrency.allowed: boolean
: If false then library won't sync state to IndexedDB if it detects that another instance of Window/Tab is already syncing. Default is false. -
concurrency.refreshRate: number
: Time in ms how often library updates timestamp. This shouldn't be less than 1000ms. Default is 5000ms. -
concurrency.trackKey: string
: Name of IndexedDB key that holds the timestamp data. Default is 'ConcurrencyTimestamp'. -
concurrency.failInitialisationIfNoLock: boolean
: If the library detects that another instance of application already exists (e.g. running in different tab/window) and this is set to true then application won't start up. Default is false. -
idb.dbName
: IndexedDB database name. Use it if your application already uses IndexedDB and you want to keep everything together. Default value isNgrxStoreIdb
. -
idb.storeName
: IndexedDb store name. Use it if your application already uses IndexedDB and you want to keep everything together. Default value isStore
.
NgrxStoreIdbService
This service broadcasts information every time ngrx-store-idb syncs store to IndexedDB.
onSync(): Observable<NgrxStoreIdbSyncEvent>
Subscribe to observable returned by this method to receive NgrxStoreIdbSyncEvent
events every time when store is synced.
These are properties of NgrxStoreIdbSyncEvent
:
-
success: boolean
: indicates if synchronisation was successful. Falsy value means that data wasn't written. -
action: Action
: holds the action that triggered the synchronisation. You could use this to wait for synchronisation after some user action e.g. wait until store is synchronised after logout to close the page.
onLockAcquired(): Observable<boolean>
Subscribe to observable returned by this method to receive information whether current instance was able to acquire lock. If the value returned is true then current instance of application is the one that will sync its state to IndexedDB. False means that some other instance is already running.
canConcurrentlySync(): boolean
Retuns true if current instance has lock or if the concurrency configuration allows concurrent instances to update IndexedDB (concurrency.allowed = true
).
Usage
Target Depth Configuration
NgrxStoreIdbModule.forRoot({
keys: [
{ feature1: [{ slice11: ['slice11_1'], slice14: ['slice14_2'] }] },
{ feature2: ['slice21'] }
],
});
In this example, feature1.slice11.slice11_1
, feature1.slice14.slice14_2
, and feature2.slice21
will be synced to IndexedDB.