From e4dee02417f6d64b224d1fea4f03083f00ec4b64 Mon Sep 17 00:00:00 2001 From: arturovt Date: Mon, 18 Mar 2024 21:47:57 +0200 Subject: [PATCH] docs(storage-plugin): update to explain standalone API --- docs/plugins/storage.md | 303 ++++++++++++++++++++++++---------------- 1 file changed, 179 insertions(+), 124 deletions(-) diff --git a/docs/plugins/storage.md b/docs/plugins/storage.md index e7cf8c26c..74ce9d2c2 100644 --- a/docs/plugins/storage.md +++ b/docs/plugins/storage.md @@ -5,15 +5,36 @@ Back your stores with `localStorage`, `sessionStorage` or any other mechanism yo ## Installation ```bash -npm install @ngxs/storage-plugin --save +npm i @ngxs/storage-plugin # or if you are using yarn yarn add @ngxs/storage-plugin + +# or if you are using pnpm +pnpm i @ngxs/storage-plugin ``` ## Usage -Import the `NgxsStoragePluginModule` into your app module like: +When calling `provideStore`, include `withNgxsStoragePlugin` in your app config: + +```ts +import { provideStore } from '@ngxs/store'; +import { withNgxsStoragePlugin } from '@ngxs/storage-plugin'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [], + withNgxsStoragePlugin({ + keys: '*' + }) + ) + ] +}; +``` + +If you are still using modules, include the `NgxsStoragePluginModule` plugin in your root app module: ```ts import { NgxsModule } from '@ngxs/store'; @@ -66,75 +87,91 @@ export class DetectivesState {} In order to persist all states, you have to provide `*` as the `keys` option: ```ts -@NgModule({ - imports: [NgxsStoragePluginModule.forRoot({ keys: '*' })] -}) -export class AppModule {} +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: '*' + }) + ) + ] +}; ``` But what if we wanted to persist only `NovelsState`? Then we would have needed to pass its name to the `keys` option: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: ['novels'] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: ['novels'] + }) + ) ] -}) -export class AppModule {} +}; ``` It's also possible to provide a state class as opposed to its name: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: [NovelsState] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: [NovelsState] + }) + ) ] -}) -export class AppModule {} +}; ``` And if we wanted to persist `NovelsState` and `DetectivesState`: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: ['novels', 'detectives'] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: ['novels', 'detectives'] + }) + ) ] -}) -export class AppModule {} +}; ``` Or using state classes: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - key: [NovelsState, DetectivesState] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: [NovelsState, DetectivesState] + }) + ) ] -}) -export class AppModule {} +}; ``` You can even combine state classes and strings: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - key: ['novels', DetectivesState] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: ['novels', DetectivesState] + }) + ) ] -}) -export class AppModule {} +}; ``` This is very useful for avoiding the persistence of runtime-only states that should not be saved to any storage. @@ -142,50 +179,58 @@ This is very useful for avoiding the persistence of runtime-only states that sho It is also possible to provide storage engines for individual keys. For example, if we want to persist `NovelsState` in the local storage and `DetectivesState` in the session storage, the signature for the key will appear as follows: ```ts -import { LOCAL_STORAGE_ENGINE, SESSION_STORAGE_ENGINE } from '@ngxs/storage-plugin'; +import { + withNgxsStoragePlugin, + LOCAL_STORAGE_ENGINE, + SESSION_STORAGE_ENGINE +} from '@ngxs/storage-plugin'; -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: [ - { - key: 'novels', // or `NovelsState` - engine: LOCAL_STORAGE_ENGINE - }, - { - key: DetectivesState, // or `detectives` - engine: SESSION_STORAGE_ENGINE - } - ] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: [ + { + key: 'novels', // or `NovelsState` + engine: LOCAL_STORAGE_ENGINE + }, + { + key: DetectivesState, // or `detectives` + engine: SESSION_STORAGE_ENGINE + } + ] + }) + ) ] -}) -export class AppModule {} +}; ``` `LOCAL_STORAGE_ENGINE` and `SESSION_STORAGE_ENGINE` are injection tokens that resolve to `localStorage` and `sessionStorage`, respectively. These tokens should not be used in apps with server-side rendering as it will throw an exception stating that these symbols are not defined in the global scope. Instead, it is recommended to provide a custom storage engine. The `engine` property can also refer to classes that implement the `StorageEngine` interface: ```ts -import { StorageEngine } from '@ngxs/storage-plugin'; +import { withNgxsStoragePlugin, StorageEngine } from '@ngxs/storage-plugin'; @Injectable({ providedIn: 'root' }) export class MyCustomStorageEngine implements StorageEngine { // ... } -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: [ - { - key: 'novels', - engine: MyCustomStorageEngine - } - ] - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [NovelsState, DetectivesState], + withNgxsStoragePlugin({ + keys: [ + { + key: 'novels', + engine: MyCustomStorageEngine + } + ] + }) + ) ] -}) -export class AppModule {} +}; ``` ### Namespace Option @@ -193,15 +238,17 @@ export class AppModule {} The namespace option should be provided when the storage plugin is used in micro frontend applications. The namespace may equal the app name and will prefix keys for state slices: ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: '*', - namespace: 'auth' - }) +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [], + withNgxsStoragePlugin({ + keys: '*', + namespace: 'auth' + }) + ) ] -}) -export class AppModule {} +}; ``` ### Custom Storage Engine @@ -209,7 +256,7 @@ export class AppModule {} You can add your own storage engine by implementing the `StorageEngine` interface: ```ts -import { NgxsStoragePluginModule, StorageEngine, STORAGE_ENGINE } from '@ngxs/storage-plugin'; +import { withNgxsStoragePlugin, StorageEngine, STORAGE_ENGINE } from '@ngxs/storage-plugin'; @Injectable() export class MyStorageEngine implements StorageEngine { @@ -222,16 +269,21 @@ export class MyStorageEngine implements StorageEngine { } } -@NgModule({ - imports: [NgxsModule.forRoot([]), NgxsStoragePluginModule.forRoot({ keys: '*' })], +export const appConfig: ApplicationConfig = { providers: [ + provideStore( + [], + withNgxsStoragePlugin({ + keys: '*' + }) + ), + { provide: STORAGE_ENGINE, useClass: MyStorageEngine } ] -}) -export class MyModule {} +}; ``` ### Serialization Interceptors @@ -242,28 +294,30 @@ You can define your own logic before or after the state get serialized or deseri - `afterSerialize`: Use this option to alter the state after it gets deserialized. For instance, you can use it to instantiate a concrete class. ```ts -@NgModule({ - imports: [ - NgxsStoragePluginModule.forRoot({ - keys: ['counter'], - beforeSerialize: (obj, key) => { - if (key === 'counter') { - return { - count: obj.count < 10 ? obj.count : 10 - }; - } - return obj; - }, - afterDeserialize: (obj, key) => { - if (key === 'counter') { - return new CounterInfoStateModel(obj.count); +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [CounterState], + withNgxsStoragePlugin({ + keys: ['counter'], + beforeSerialize: (obj, key) => { + if (key === 'counter') { + return { + count: obj.count < 10 ? obj.count : 10 + }; + } + return obj; + }, + afterDeserialize: (obj, key) => { + if (key === 'counter') { + return new CounterInfoStateModel(obj.count); + } + return obj; } - return obj; - } - }) + }) + ) ] -}) -export class AppModule {} +}; ``` ### Migrations @@ -272,28 +326,29 @@ You can migrate data from one version to another during the startup of the store is a strategy to migrate my state from `animals` to `newAnimals`. ```ts -@NgModule({ - imports: [ - NgxsModule.forRoot([]), - NgxsStoragePluginModule.forRoot({ - keys: '*', - migrations: [ - { - version: 1, - key: 'zoo', - versionKey: 'myVersion', - migrate: state => { - return { - newAnimals: state.animals, - version: 2 // Important to set this to the next version! - }; +export const appConfig: ApplicationConfig = { + providers: [ + provideStore( + [], + withNgxsStoragePlugin({ + keys: '*', + migrations: [ + { + version: 1, + key: 'zoo', + versionKey: 'myVersion', + migrate: state => { + return { + newAnimals: state.animals, + version: 2 // Important to set this to the next version! + }; + } } - } - ] - }) + ] + }) + ) ] -}) -export class MyModule {} +}; ``` In the migration strategy, we define: