Reputation: 121
I am using idb (indexedDB) as the persistence storage plugin for NGXS Store, when using '*' to represent all state keys, upon reloading the page the state is reset to it's default values. When using specified state as in code below, the initial stored state does not store the default state in the indexedDb storage, hence mutating the state does not work.
indexeddb.persistence.ts
import { Injectable } from '@angular/core';
import { ICN_INDEXEDDB } from '@igus/icn-ui';
import type { StorageEngine } from '@ngxs/storage-plugin';
import type { DBSchema, IDBPDatabase } from 'idb';
import { openDB } from 'idb';
/**
* NGXS Indexed DB Store Interface
*/
interface StoreBD extends DBSchema {
keyval: {
key: string;
value: unknown;
};
}
const KEY_VAL = 'keyval';
/**
* Indexed DB Storage Engine
*/
@Injectable()
export class IndexedDBPersistence implements StorageEngine {
/**
* Databased Store
*/
readonly #indexedDB: Promise<IDBPDatabase<StoreBD>>;
constructor() {
this.#indexedDB = openDB<StoreBD>(ICN_INDEXEDDB, 1, {
upgrade: (db) => {
db.createObjectStore(KEY_VAL);
},
});
}
/**
* Get items from storage
*
* @param key key of item
*/
async getItem(key: string): Promise<unknown> {
const db = await this.#indexedDB;
return await db.get(KEY_VAL, key);
}
/**
* Store item in storage
*
* @param key key of item
* @param val value to store
*/
async setItem(key: string, val: unknown): Promise<void> {
const db = await this.#indexedDB;
await db.put(KEY_VAL, val, key);
}
}
ngxs.config.ts
import type { EnvironmentProviders } from '@angular/core';
import { ConfigurationState, TechnicalDrawingState } from '@igus/icn-app-state';
import { FileUploadState } from '@igus/icn-feature-file-upload';
import { MaterialState } from '@igus/icn-feature-material';
import { CadState } from '@igus/icn-feature-models';
import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin';
import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin';
import { withNgxsRouterPlugin } from '@ngxs/router-plugin';
import { withNgxsStoragePlugin } from '@ngxs/storage-plugin';
import { provideStore } from '@ngxs/store';
import { withNgxsWebSocketPlugin } from '@ngxs/websocket-plugin';
import { environment } from '../environments/environment';
/**
* @description States to be persisted in the browser
* FileUploadState is excluded so that users don't get stuck on 'processing' when an error occurs
* even after refresh
*/
const storedStates = [
CadState,
MaterialState,
ConfigurationState,
TechnicalDrawingState,
];
/**
* Configuration for NGXS
*
* @see https://www.ngxs.io/getting-started/installation
* @see https://www.ngxs.io/plugins
*/
export const ngxsConfig: EnvironmentProviders = provideStore(
[...storedStates, FileUploadState],
withNgxsWebSocketPlugin({
url: environment.icnDataServiceWsUrl,
typeKey: 'event', // NestJS websockets uses 'event' key
}),
withNgxsStoragePlugin({
keys: storedStates,
}),
withNgxsRouterPlugin(),
withNgxsReduxDevtoolsPlugin({
disabled: environment.production,
}),
withNgxsLoggerPlugin({
disabled: environment.production,
}),
);
app.config.ts
import type { ApplicationConfig } from '@angular/core';
import {
APP_INITIALIZER,
ErrorHandler,
importProvidersFrom,
} from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideRouter, Router, withInMemoryScrolling } from '@angular/router';
import { STORAGE_ENGINE } from '@ngxs/storage-plugin';
import * as Sentry from '@sentry/angular-ivy';
import { Angulartics2RouterlessModule } from 'angulartics2';
import { environment } from '../environments/environment';
import {
CONTACT_ID,
ICN_APP_VERSION,
KOPLA_APP_VERSION,
ROUTES,
} from './app.di';
import { appRoutes } from './app.routes';
import { ngxsConfig } from './ngxs.config';
import { IndexedDBPersistence } from './services';
export const appConfig: ApplicationConfig = {
providers: [
ngxsConfig,
provideRouter(
appRoutes,
withInMemoryScrolling({ anchorScrolling: 'enabled' }),
),
provideAnimations(),
importProvidersFrom(
FileUploadModule.forRoot({
fileUploadApi: environment.koplaUploadUrl,
}),
),
{
provide: ErrorHandler,
useValue: Sentry.createErrorHandler({
showDialog: false,
}),
},
{
provide: Sentry.TraceService,
deps: [Router],
},
{
provide: APP_INITIALIZER,
useFactory: () => () => ({}),
deps: [Sentry.TraceService],
multi: true,
},
{
provide: CONTACT_ID,
useValue: contactId,
},
{
provide: KOPLA_APP_VERSION,
useValue: KOPLA_APP_VERSION_PLACEHOLDER,
},
{
provide: ROUTES,
useValue: ROUTE_LIST,
},
{
provide: STORAGE_ENGINE,
useClass: IndexedDBPersistence,
}
],
};
Upvotes: 0
Views: 36
Reputation: 1
I too am currently trying to get IndexedDb to work with NGXS. Unfortunately the Store doesn't yet support asynchronous getItem
or setItem
methods. It expects a synchronous response such as you would get if you called localStorage.getItem
, sessionStorage.getItem
etc.
I am going to have a look at NGXS to see if I can implement support for async StorageEngine
methods and will keep you posted.
Upvotes: 0