Storage
Back your stores with localStorage, sessionStorage or any other mechanism you wish.

Installation

1
npm install @ngxs/storage-plugin --save
2
3
# or if you are using yarn
4
yarn add @ngxs/storage-plugin
Copied!

Usage

Import the NgxsStoragePluginModule into your app module like:
1
import { NgxsModule } from '@ngxs/store';
2
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
3
4
@NgModule({
5
imports: [NgxsModule.forRoot([]), NgxsStoragePluginModule.forRoot()]
6
})
7
export class AppModule {}
Copied!
It is recommended to register the storage plugin before other plugins so initial state can be picked up by those plugins.

Options

The plugin has the following optional values:
    key: State name(s) to be persisted. You can pass a string or array of strings that can be deeply nested via dot notation. If not provided, it defaults to all states using the @@STATE key.
    storage: Storage strategy to use. This defaults to LocalStorage but you can pass SessionStorage or anything that implements the StorageEngine API.
    deserialize: Custom deserializer. Defaults to JSON.parse
    serialize: Custom serializer. Defaults to JSON.stringify
    migrations: Migration strategies
    beforeSerialize: Interceptor executed before serialization
    afterDeserialize: Interceptor executed after deserialization

Key option

The key option is used to determine what states should be persisted in the storage. key shouldn't be a random string, it has to coincide with your state names. Let's look at the below example:
1
// novels.state.ts
2
@State<Novel[]>({
3
name: 'novels',
4
defaults: []
5
})
6
@Injectable()
7
export class NovelsState {}
8
9
// detectives.state.ts
10
@State<Detective[]>({
11
name: 'detectives',
12
defaults: []
13
})
14
@Injectable()
15
export class DetectivesState {}
Copied!
In order to persist all states there is no need to provide the key option, so it's enough just to write:
1
@NgModule({
2
imports: [NgxsStoragePluginModule.forRoot()]
3
})
4
export class AppModule {}
Copied!
But what if we wanted to persist only NovelsState? Then we would have needed to pass its name to the key option:
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: 'novels'
5
})
6
]
7
})
8
export class AppModule {}
Copied!
It's also possible to provide a state class as opposed to its name:
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: NovelsState
5
})
6
]
7
})
8
export class AppModule {}
Copied!
And if we wanted to persist NovelsState and DetectivesState:
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: ['novels', 'detectives']
5
})
6
]
7
})
8
export class AppModule {}
Copied!
Or using state classes:
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: [NovelsState, DetectivesState]
5
})
6
]
7
})
8
export class AppModule {}
Copied!
You can even combine state classes and strings:
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: ['novels', DetectivesState]
5
})
6
]
7
})
8
export class AppModule {}
Copied!
This is very handy to avoid persisting runtime-only states that shouldn't be saved to any storage.

Custom Storage Engine

You can add your own storage engine by implementing the StorageEngine interface.
1
import { NgxsStoragePluginModule, StorageEngine, STORAGE_ENGINE } from '@ngxs/storage-plugin';
2
3
export class MyStorageEngine implements StorageEngine {
4
get length(): number {
5
// Your logic here
6
}
7
8
getItem(key: string): any {
9
// Your logic here
10
}
11
12
setItem(key: string, val: any): void {
13
// Your logic here
14
}
15
16
removeItem(key: string): void {
17
// Your logic here
18
}
19
20
clear(): void {
21
// Your logic here
22
}
23
24
key(val: number): string {
25
// Your logic here
26
}
27
}
28
29
@NgModule({
30
imports: [NgxsModule.forRoot([]), NgxsStoragePluginModule.forRoot()],
31
providers: [
32
{
33
provide: STORAGE_ENGINE,
34
useClass: MyStorageEngine
35
}
36
]
37
})
38
export class MyModule {}
Copied!

Serialization Interceptors

You can define your own logic before or after the state get serialized or deserialized.
    beforeSerialize: Use this option to alter the state before it gets serialized.
    afterSerialize: Use this option to alter the state after it gets deserialized. For instance, you can use it to instantiate a concrete class.
1
@NgModule({
2
imports: [
3
NgxsStoragePluginModule.forRoot({
4
key: 'counter',
5
beforeSerialize: (obj, key) => {
6
if (key === 'counter') {
7
return {
8
count: obj.count < 10 ? obj.count : 10
9
};
10
}
11
return obj;
12
},
13
afterDeserialize: (obj, key) => {
14
if (key === 'counter') {
15
return new CounterInfoStateModel(obj.count);
16
}
17
return obj;
18
}
19
})
20
]
21
})
22
export class AppModule {}
Copied!

Migrations

You can migrate data from one version to another during the startup of the store. Below is a strategy to migrate my state from animals to newAnimals.
1
@NgModule({
2
imports: [
3
NgxsModule.forRoot([]),
4
NgxsStoragePluginModule.forRoot({
5
migrations: [
6
{
7
version: 1,
8
key: 'zoo',
9
versionKey: 'myVersion',
10
migrate: state => {
11
return {
12
newAnimals: state.animals,
13
version: 2 // Important to set this to the next version!
14
};
15
}
16
}
17
]
18
})
19
]
20
})
21
export class MyModule {}
Copied!
In the migration strategy, we define:
    version: The version we are migrating
    versionKey: The identifier for the version key (Defaults to 'version')
    migrate: A function that accepts a state and expects the new state in return.
    key: The key for the item to migrate. If not specified, it takes the entire storage state.
Note: Its important to specify the strategies in the order of which they should progress.
Last modified 1yr ago