2

I am using NGRX in my Angular app and I allow users to switch between different versions of my store which are stored and reloaded from a database. I would like to be able to detect when changes have been made to the store but have not been saved so I can prompt the user to save before exiting or opening a different version.

My first thought was to have a property in my store, but I have so many reducers that it would be a nightmare to update that property every time. Is there a way to set a flag that would be triggered before any reducer is called to set the state as dirty or not saved? Or any better solution to know that a save is required before exiting?

Envil
  • 2,687
  • 1
  • 30
  • 42
james Makinde
  • 943
  • 3
  • 17
  • 36

1 Answers1

0

UPDATE: After thoroughful considerations, I think your requirement is problematic: if you store the backedUp flag in your state, then once the flag is updated (to false), it will trigger the store to be backed up. Then after the store has been backed up, the flag should be updated to true, this will trigger the store to be backed up again. This will happens on and on forever.


This's what I have in mind at the moment:

  1. Create the backUp store feature:

You can put the backedUp flag in there, your store might look like:

{
  ...
  backUp: {
    backedUp: boolean;
    backingUp: boolean;  // optional
    lastVersion: string; // optional
    ...
  }
}
  1. Create BACK_UP_STORE_ACTION and STORE_BACKED_UP_ACTION.
  2. Create two effects to watch and back up the store:

You may want to write it like this:

  constructor(private actions$: Actions,
              private store: Store<fromRoot.State>,
              private backUpStoreService: BackUpStoreService) {
  }

  @Effect()
  watchState$ = this.store
    .debounceTime(1000) // save store 1 second after the last change, preventing API spamming
    .map(state => new storeBackUp.BackUpStoreAction(state));

  @Effect()
  backUpState$ = this.actions$
    .ofType(storeBackUp.BACK_UP_STORE_ACTION)
    .concatMap((action: storeBackUp.BackUpStoreAction) => this.backUpStoreService.backUp(action.payload))
    .map(response => new storeBackUp.StoreBackedUpAction(response));
  1. Create a meta reducer to update the backedUp flag.

Here is the sudo code for the meta reducer, this meta-reducer check on every action, if the action is BACK_UP_STORE_ACTION, it will set the backedUp flag to false; if the action is STORE_BACKED_UP_ACTION, it will set the backedUp flag to true.

export function storeBackUpMetaReducer(reducer: ActionReducer<State>): ActionReducer<State> {
  return function (state: State, action: any): State {
    if (action.type === storeBackUp.STORE_BACKED_UP_ACTION) {
      state.backUp.backedUp = true;
    } else if (action.type === storeBackUp.BACK_UP_STORE_ACTION) {
      state.backUp.backedUp = false;
    }
    return reducer(state, action);
  };
}
  1. Now you can watch on the backedUp flag to prompt the use when they want to exit before the store has been saved.
Envil
  • 2,687
  • 1
  • 30
  • 42
  • I don't want to automatically backup the store. It will be user initiated if that makes any difference to your solution. From your update, it seems this will keep happening. Would it be possible for the watchstate to not call the backup store action ONLY if the change is the property backedup of the store is going from false to true. In which case the store is clean. More or less, that variable change from false to true should not trigger a save required. Is it possible to keep track of the various states that way? – james Makinde Jul 12 '18 at 13:00
  • @jamesMakinde maybe you can use a filter in my `watchState$` effect to filter out that. – Envil Jul 14 '18 at 09:37