Skip to content

Automatically synchronize an NgRx root state with the application device's storage.

License

Notifications You must be signed in to change notification settings

D4H/ion-ngx-storage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Codeship Status for D4H/ion-ngx-storage npm

@d4h/ion-ngx-storage

ion-ngx-storage is a module for Ionic 4/Angular applications which copies an application's NgRx root state to the native device or browser through Ionic/Storage.

Installation

npm install --save @d4h/ion-ngx-storage

Configuration

interface StorageConfig<T extends object = {}> {
  name: string;

  storage?: {
    description?: string;
    driver?: string | Array<string>;
    name?: string;
    size?: number;
    version?: number;
  }
}
  • name: string: The name of your application. Used internally as an Ionic Storage table key. All data is stored per application as a single object.
  • storage?: StorageConfig: Ionic Storage configuration.

Default Configuration

const defaultConfig: StorageConfig = {
  name: 'ion_ngx_storage',

  storage: {
    name: 'ion_ngx_storage'
  }
};

Use

Import StoreModule or call StoreModule.forRoot() with a configuration. ion-ngx-storage read and write the application state without any additional configuration. After effects initialization, ion-ngx-storage writes a serialized copy of the root state to the device after each action dispatch.

Without Extra Configuration

import { StorageModule } from '@d4h/ion-ngx-storage';

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers }),
    EffectsModule.forRoot(effects),
    StorageModule
  ]
})
export class AppModule {}

With Extra Configuration

import { StorageConfig, StorageModule } from '@d4h/ion-ngx-storage';

// Optional configuration
const storageConfig: StorageConfig<AppState> = {
  name: 'my_application_name'
};

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers }),
    EffectsModule.forRoot(effects),
    StorageModule.forRoot(storageConfig)
  ]
})
export class AppModule {}

Your application must import StoreModule.forRoot and EffectsModule.forRoot in order for ion-ngx-storage to function.

Waiting for Hydration

Internally, ion-ngx-storage operates in the following manner:

  1. Register StorageEffects and HydrateEffects.
  2. Dispatch Read from HydrateEffects.
  3. Read state from storage and dispatch ReadResult.
  4. Merge the result into the application state via meta-reducer.
  5. If { hydrated: true } then dispatch ReadSuccess.

ReadSuccess Action

ion-ngx-storage makes the ReadSuccess action public for use in NgRx effects.

import { ReadSuccess } from '@d4h/ion-ngx-storage';

@Injectable()
export class AppEffects {
  // Keep up splash screen until after hydration.
  init$: Observable<Action> = createEffect(() => this.actions$.pipe(
      ofType(ReadSuccess),
      tap(() => {
        this.platform.ready().then(() => {
          this.statusBar.styleLightContent();
          this.splashScreen.hide();
        });
      })
    ),
    { dispatch: false }
  );

  constructor(/* ... */) {}
}

Inject Configuration

The public STORAGE_CONFIG token allows injection of the configuration in cases of module composition.

import { STORAGE_CONFIG, StorageConfig, StorageModule } from '@d4h/ion-ngx-storage';

@NgModule({
  imports: [
    StorageModule
  ]
})
export class AppFeatureModule {
  static forFeature(config: FeatureConfig): ModuleWithProviders {
    return {
      ngModule: AppFeatureModule,
      providers: [
        { provide: STORAGE_CONFIG, useValue: config.storage }
      ]
    };
  }
}

Selecting Storage Status

ion-ngx-storage makes StorageState available for cases where you need to select or extend the state:

import { StorageState } from '@d4h/ion-ngx-storage';

export interface AppState extends StorageState {
  // ...
}

After this you can employ the getHydrated and getStorageState selectors.

Defer Store Access

Although ion-ngx-storage hydrates data from storage once NgRx Effects dispatches ROOT_EFFECTS_INIT, the asynchronous nature of Angular and NgRx make it likely your application will attempts to read from the state it is ready. Applications which rely on the NgRx store to determine i.e. authentication status must be modified in a way which defers assessment until after hydration.

In both cases below:

  1. filter(Boolean) leads to only truthy values emitting.
  2. Once this happens, switchMap replaces the prior observable with a new one that contains the actual assessment of authentication status.

AccountFacade

import { StorageFacade } from '@d4h/ion-ngx-storage';

@Injectable({ providedIn: 'root' })
export class AccountFacade {
  readonly authenticated$: Observable<boolean> = this.storage.hydrated$.pipe(
    filter(Boolean),
    switchMap(() => this.store.select(getAuthenticated))
  );

  constructor(
    private readonly storage: StorageFacade,
    private readonly store: Store<AppState>
  ) {}
}

AuthenticatedGuard

import { AccountFacade } from '@app/store/account';

@Injectable({ providedIn: 'root' })
export class AuthenticatedGuard implements CanActivate {
  private readonly authenticated$: Observable<boolean>;

  constructor(
    private readonly accounts: AccountFacade,
    private readonly router: Router
    ) {
      this.authenticated$ = this.store.pipe(
        select(getHydrated),
        filter(Boolean),
        switchMap(() => this.store.select(getAuthentication))
      );
    }

  canActivate(): Observable<boolean | UrlTree> {
    return this.authenticated$.pipe(
      map((authenticated: boolean): boolean | UrlTree => {
        return authenticated || this.router.parseUrl('/login');
      })
    );
  }
}

Logout/Reinitialization

Many applications have some kind of logout action which resets the application to its in initial state. In these cases ion-ngx-storage resets to { hydrated: false }, meaning it will no longer write device state to storage. In these cases you have to dispatch one Clear or Read:

  • Clear: Wipe the stored application state and triggers Read with an initial empty value.
  • Read: Reads the logged-out state and triggers reducer setting { hydrated: true }.

The difference in practice is whether you want to remove all content stored on the device.

import { Read } from '@d4h/ion-ngx-storage';

class LoginEffects {
  logout$: Observable<Action> = createEffect(() => this.actions$.pipe(
    ofType(Logout),
    switchMap(() => [
      Read(),
      Navigate({ path: ['/login', 'username'] })
    ])
  ));
}

Support and Feedback

Feel free to email [email protected], open an issue or tweet @d4h.

License

Copyright (C) 2019 D4H

Licensed under the MIT license.

About

Automatically synchronize an NgRx root state with the application device's storage.

Resources

License

Stars

Watchers

Forks

Packages

No packages published