ngxs-labs / async-storage-plugin Goto Github PK
View Code? Open in Web Editor NEW⏱ WIP: Async Storage Plugin
License: MIT License
⏱ WIP: Async Storage Plugin
License: MIT License
Let's imagine we have 2 states: A and B
interface TestModel {
conter: number;
}
@State<TestModel>({
name: 'a',
defaults: {
counter: 0,
},
})
export class AState {}
@State<TestModel>({
name: 'b',
defaults: {
counter: 0,
},
})
export class BState {}
The app is configured to save the BState changes via HTTP on the backend
NgxsAsyncStoragePluginModule.forRoot(
MyEngine,
{
key: 'b',
}
),
Let's imagine it takes 1 second to pull data from the backend.
After the initialization we have next state tree:
{
a: {
counter: 0
},
b: {
counter: 0
}
}
Then we start pulling the B state which has the counter value 1 on the backend.
While B state is pulling we have updated several times the state A:
{
a: {
counter: 3
},
b: {
counter: 0
}
}
Then B state data received and plugin updates the state. And it reverts our changes from state A:
{
a: {
counter: 0
},
b: {
counter: 1
}
}
That is because of:
where 'previousState' is the state that was at the moment of pulling, not the latest one.
The expected behavior is to update the B state only:
{
a: {
counter: 3
},
b: {
counter: 1
}
}
It seems to always return the default value for me, when I console.log the output on the Auth Guard, instead of the one saved.
But Redux DevTools shows it loaded the correct value on the INIT state though.
Hello,
I am trying to use the async-storage-plugin together with cordova-sqlite-storage.
The Storage Engine needs to get initialized (open DB etc.)
Where is the right place to do this? AsyncStoragePlugin always wants to access before the DB is available.
I was trying to init the DB with angular APP_INITIALIZER but this gets triggered after Ngxs init routines.
All states are read from storage on InitState
and on UpdateState
. This brings the app to the problem of inconsistent data when state was changed (in memory) but then overwritten from storage. To fix this I propose: on UpdateState get only addedStates
from storage.
Also, it would be nice to:
I have added a test on this.
import { TestBed } from '@angular/core/testing';
import { NgxsAsyncStoragePluginModule } from '@ngxs-labs/async-storage-plugin';
import { NgxsModule, State, Store, UpdateState } from '@ngxs/store';
describe('read state from storage once', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
NgxsModule.forRoot([StateToStore]),
NgxsAsyncStoragePluginModule.forRoot(KeyValueStorageMock, {
key: [StateToStore.name]
})
],
providers: [KeyValueStorageMock]
});
});
it(`does not get again ${StateToStore.name} when ${UpdateState.name} dispatched`, async () => {
const store = TestBed.get(Store) as Store;
// was get on InitState
expect(getItemSpy).toHaveBeenCalledWith(StateToStore.name);
getItemSpy.calls.reset();
await store.dispatch(new UpdateState()).toPromise();
expect(getItemSpy).not.toHaveBeenCalledWith(StateToStore.name);
});
});
const getItemSpy = jasmine.createSpy('getItem');
class KeyValueStorageMock {
getItem(key: string) {
console.log(key);
return getItemSpy(key);
}
setItem() {}
}
@State<string>({
defaults: 'default',
name: StateToStore.name
})
class StateToStore {
constructor() {}
}
I was able to manage our own storage service that provides functionality to support async storage with the help of StorageMap library that use IndexedDb and AsyncStorageEngine.
Install StorageMap .
Install AsyncStorageEngine
Create a new storage service and implement AsyncStorageEngine with missing members like this:
export class StorageService implements AsyncStorageEngine {
constructor(private storageMap: StorageMap}
NgxsModule.forRoot(
[
// Add your state classes here
],
{
developmentMode: !environment.production,
selectorOptions: {
// https://www.ngxs.io/concepts/select#selector-options
// These Selector Settings are recommended in preparation for NGXS v4
suppressErrors: false,
injectContainerState: false,
},
},
),
// https://github.com/ngxs-labs/async-storage-plugin
NgxsAsyncStoragePluginModule.forRoot(StorageService),
// https://github.com/ngxs-labs/async-storage-plugin
NgxsDataPluginModule.forRoot([NGXS_DATA_STORAGE_PLUGIN]),
@Persistence({
useClass: StorageService,
})
@StateRepository()
@State<MyStateModel>({...
Et voilà!
You can now use your async StorageService with NGXS!
Note: This will not work with IE11 and target type ES5 because ngxs-lab/data @persistence decorator use the Package class that is only available with ES6 (ES2015). I was not able get this work event if I tried with the following packages:
Originally posted by @SteveLemire in ngxs-labs/data#299 (comment)
Using the Sample : Custom Ionic Storage Engine
the data gets wiped after 3-4 reloads of the page , the same code using ```@ngxs/storage-plugin```` works as expected.
Feels like there is a race condition
Been waiting for this for quite some time, how's the progress so far?
Tried to find it on NPM, but still yet no publish has been done, even though it looks like it's quite finished.
It looks like nothing more is being developed on the current package. Is there an alternative package?
Currently the key
option for the importing of the plugin only allows for State Tokens and strings. This causes an error (#123) if you pass in a StateModel
.
It should be fairly easy to suppose a StateModel
object since it has a name
attribute which is the value that needs to be examined rather than the key
itself.
for (const key of keys) {
let val = nextState;
if (key !== '@@STATE') {
val = getValue(nextState, key);
}
try {
this._asyncEngine.setItem(key, options.serialize(val));
}
catch (e) {
console.error('Error ocurred while serializing the store value, value not updated.');
}
}
Just check to see if the key is a StateModel
and if it is, the "real name" is key.name
rather than key
.
I have added unit test on this
Test failed with error Error: Expected spy setItem not to have been called
:
import { TestBed } from '@angular/core/testing';
import { NgxsAsyncStoragePluginModule } from '@ngxs-labs/async-storage-plugin';
import { Action, NgxsModule, State, StateContext, Store } from '@ngxs/store';
describe('save to storage on any state change', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
NgxsModule.forRoot([
StateToStore, InMemoryState
]),
NgxsAsyncStoragePluginModule.forRoot(KeyValueStorageMock, {
key: [StateToStore.name]
})
],
providers: [KeyValueStorageMock]
});
});
it(`does not save ${StateToStore.name} when ${UpdateInMemoryState.name} dispatched`, async () => {
const store = TestBed.get(Store) as Store;
await store.dispatch(new UpdateInMemoryState).toPromise();
expect(setItemSpy).not.toHaveBeenCalled();
});
});
const setItemSpy = jasmine.createSpy('setItem');
class KeyValueStorageMock {
getItem() {}
setItem() {
return setItemSpy();
}
}
@State<string>({
defaults: 'default',
name: StateToStore.name
})
class StateToStore {
constructor() {}
}
class UpdateInMemoryState {
static type = 'UpdateInMemoryStore';
constructor() {}
}
@State<string>({
defaults: '',
name: InMemoryState.name
})
class InMemoryState {
constructor() {}
@Action(UpdateInMemoryState)
update(ctx: StateContext<string>) {
ctx.setState('some value');
}
}
I'm using this plugin in my ionic 4 project, my app will crash when trying to save large array (500++ elements) to ngxs state with async-storage-plugin (use ionic storage plugin example given). When i'm using the @ngxs-labs/storage-plugin which internally using localStorage and sessionStorage, the app is working fine without problem.
As a user of the library, I would like to be able to provide different storage engines for specific stores, so that I can store secret states like access tokens in secret storage (in Ionic for example) while storing other state in usual storage.
Currently, it is only possible to specify one storage engine within the initialization of the module:
NgxsAsyncStoragePluginModule.forRoot(IonicStorageEngine, {
key: [SettingsState, AuthenticationState]
})
However, say I want the AuthenticationState stored in a secured storage. I need another Storage engine for that. I would suggest to make the following configuration (or something similar) possible:
NgxsAsyncStoragePluginModule.forRoot({
storageStrategies: [
{
engine: IonicStorageEngine,
key: [SettingsState]
},
{
engine: IonicSecureStorageEngine,
key: [AuthenticationState]
},
]
})
I think that key option is not working.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.