Giter VIP home page Giter VIP logo

async-storage-plugin's People

Contributors

eranshmil avatar marcjulian avatar renovate-bot avatar renovate[bot] avatar splincode avatar yoglib avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

async-storage-plugin's Issues

Plugin reverts last changes on other states

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:

nextState = setValue(previousState, key, val);

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
  }
}

init storage engine before ngxs starts

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.

Read same state from store for multiple times

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:

  1. make sure that state was read once from storage
  2. allow writing state to storage only after data was read from storage.

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() {}
}

How to create async storage with NGXS

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.

  1. Install StorageMap .

  2. Install AsyncStorageEngine

  3. Create a new storage service and implement AsyncStorageEngine with missing members like this:

export class StorageService implements AsyncStorageEngine {
  constructor(private storageMap: StorageMap}
  1. In your app.module.ts, configure your NgxsModule and other packages like this:
    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]),
  1. In your state class, add the following decorators:
@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)

Keeps wiping data

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

Progress ?

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.

Support States within the key parameter

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.

Plugin saves state A to storage when state B (should not be stored) was changed.

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');
  }
}

Ionic App Crashed when saving large array

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.

Any help will be highly appreciate. Thank you.
Error-snapshot

Add Ability to Add Multiple Engines

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]
    },
  ]
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.