Skip to content

Commit

Permalink
format, and add versionKey
Browse files Browse the repository at this point in the history
  • Loading branch information
larscom committed Dec 28, 2023
1 parent 772d18b commit f17cd40
Show file tree
Hide file tree
Showing 17 changed files with 600 additions and 588 deletions.
5 changes: 3 additions & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"useTabs": false,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none",
"disableLanguages": ["html"]
"singleQuote": true,
"semi": false,
"disableLanguages": []
}
77 changes: 41 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
> **Highly configurable** state sync library between `localStorage/sessionStorage` and `@ngrx/store` (Angular)
## Features

- ✓ Sync with `localStorage` and `sessionStorage`
- ✓ **Storage** option per feature state, for example:
- feature1 to `sessionStorage`
- feature2 to `localStorage`
- ✓ Exclude **deeply** nested properties
- ✓ Exclude **deeply** nested properties
- ✓ [Sync Reactive Forms](#Sync-Reactive-Forms) (needs additional library)

## Dependencies
Expand All @@ -37,17 +38,17 @@ Choose the version corresponding to your Angular version
Include `storageSyncReducer` in your meta-reducers array in `StoreModule.forRoot`

```ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { routerReducer } from '@ngrx/router-store';
import { storageSync } from '@larscom/ngrx-store-storagesync';
import * as fromFeature1 from './feature/reducer';
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { StoreModule } from '@ngrx/store'
import { routerReducer } from '@ngrx/router-store'
import { storageSync } from '@larscom/ngrx-store-storagesync'
import * as fromFeature1 from './feature/reducer'

export const reducers: ActionReducerMap<IRootState> = {
router: routerReducer,
feature1: fromFeature1.reducer
};
}

export function storageSyncReducer(reducer: ActionReducer<IRootState>): ActionReducer<IRootState> {
// provide all feature states within the features array
Expand All @@ -62,13 +63,13 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>): ActionRe
],
// defaults to localStorage
storage: window.localStorage
});
})

return metaReducer(reducer);
return metaReducer(reducer)
}

// add storageSyncReducer to metaReducers
const metaReducers: MetaReducer<any>[] = [storageSyncReducer];
const metaReducers: MetaReducer<any>[] = [storageSyncReducer]

@NgModule({
imports: [BrowserModule, StoreModule.forRoot(reducers, { metaReducers })]
Expand All @@ -83,11 +84,11 @@ export interface IStorageSyncOptions<T> {
/**
* By default, states are not synced, provide the feature states you want to sync.
*/
features: IFeatureOptions<T>[];
features: IFeatureOptions<T>[]
/**
* Provide the storage type to sync the state to, it can be any storage which implements the 'Storage' interface.
*/
storage: Storage;
storage: Storage
/**
* Give the state a version. Version will be checked before rehydration.
*
Expand All @@ -100,27 +101,31 @@ export interface IStorageSyncOptions<T> {
*
* Version from Storage = 1 and Config.version = 1 --> Hydrate
*/
version?: number;
version?: number
/**
* Under which key the version should be saved into storage
*/
versionKey?: string
/**
* Function that gets executed on a storage error
* @param error the error that occurred
*/
storageError?: (error: any) => void;
storageError?: (error: any) => void
/**
* Restore last known state from storage on startup
*/
rehydrate?: boolean;
rehydrate?: boolean
/**
* Serializer for storage keys
* @param key the storage item key
*/
storageKeySerializer?: (key: string) => string;
storageKeySerializer?: (key: string) => string
/**
* Custom state merge function after rehydration (by default it does a deep merge)
* @param state the next state
* @param rehydratedState the state resolved from a storage location
*/
rehydrateStateMerger?: (state: T, rehydratedState: T) => T;
rehydrateStateMerger?: (state: T, rehydratedState: T) => T
}
```

Expand All @@ -129,7 +134,7 @@ export interface IFeatureOptions<T> {
/**
* The name of the feature state to sync
*/
stateKey: string;
stateKey: string
/**
* Filter out (ignore) properties that exist on the feature state.
*
Expand All @@ -140,41 +145,41 @@ export interface IFeatureOptions<T> {
* // Filter/ignore only key 'loading' inside object 'auth'
* ['auth.loading']
*/
excludeKeys?: string[];
excludeKeys?: string[]
/**
* Provide the storage type to sync the feature state to,
* it can be any storage which implements the 'Storage' interface.
*
* It will override the storage property in StorageSyncOptions
* @see IStorageSyncOptions
*/
storageForFeature?: Storage;
storageForFeature?: Storage
/**
* Sync to storage will only occur when this function returns true
* @param featureState the next feature state
* @param state the next state
*/
shouldSync?: (featureState: T[keyof T], state: T) => boolean;
shouldSync?: (featureState: T[keyof T], state: T) => boolean
/**
* Serializer for storage keys (feature state),
* it will override the storageKeySerializer in StorageSyncOptions
* @see IStorageSyncOptions
*
* @param key the storage item key
*/
storageKeySerializerForFeature?: (key: string) => string;
storageKeySerializerForFeature?: (key: string) => string
/**
* Serializer for the feature state (before saving to a storage location)
* @param featureState the next feature state
*/
serialize?: (featureState: T[keyof T]) => string;
serialize?: (featureState: T[keyof T]) => string
/**
* Deserializer for the feature state (after getting the state from a storage location)
*
* ISO Date objects which are stored as a string gets revived as Date object by default.
* @param featureState the feature state retrieved from a storage location
*/
deserialize?: (featureState: string) => T[keyof T];
deserialize?: (featureState: string) => T[keyof T]
}
```

Expand All @@ -192,7 +197,7 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
{ stateKey: 'feature2' } // to localStorage because of 'default' which is set below.
],
storage: window.localStorage // default
})(reducer);
})(reducer)
}
```

Expand All @@ -211,13 +216,13 @@ const state: IRootState = {
message: 'hello' // excluded
}
}
};
}

export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
return storageSync<IRootState>({
features: [{ stateKey: 'feature1', excludeKeys: ['auth.loading', 'message'] }],
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand All @@ -235,20 +240,20 @@ const state: IRootState = {
message: 'hello'
}
}
};
}

export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
return storageSync<IRootState>({
features: [
{
stateKey: 'feature1',
shouldSync: (feature1, state) => {
return feature1.rememberMe || state.checkMe;
return feature1.rememberMe || state.checkMe
}
}
],
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand All @@ -266,7 +271,7 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
}
],
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand All @@ -284,7 +289,7 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
}
],
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand All @@ -298,7 +303,7 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
features: [{ stateKey: 'feature1' }],
storageKeySerializer: (key: string) => `abc_${key}`,
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand All @@ -311,10 +316,10 @@ export function storageSyncReducer(reducer: ActionReducer<IRootState>) {
return storageSync<IRootState>({
features: [{ stateKey: 'feature1' }],
rehydrateStateMerger: (state: IRootState, rehydratedState: IRootState) => {
return { ...state, ...rehydratedState };
return { ...state, ...rehydratedState }
},
storage: window.localStorage
})(reducer);
})(reducer)
}
```

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngrx-store-storagesync",
"version": "14.1.0",
"version": "14.0.4",
"scripts": {
"ng": "ng",
"start": "ng serve",
Expand Down
6 changes: 3 additions & 3 deletions projects/ngrx-store-storagesync/src/lib/actions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const INIT_ACTION = '@ngrx/store/init';
export const INIT_ACTION_EFFECTS = '@ngrx/effects/init';
export const UPDATE_ACTION = '@ngrx/store/update-reducers';
export const INIT_ACTION = '@ngrx/store/init'
export const INIT_ACTION_EFFECTS = '@ngrx/effects/init'
export const UPDATE_ACTION = '@ngrx/store/update-reducers'
14 changes: 7 additions & 7 deletions projects/ngrx-store-storagesync/src/lib/feature-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export interface IFeatureOptions<T> {
/**
* The name of the feature state to sync
*/
stateKey: string;
stateKey: string
/**
* Filter out (ignore) properties that exist on the feature state.
*
Expand All @@ -13,39 +13,39 @@ export interface IFeatureOptions<T> {
* // Filter/ignore only key 'loading' inside object 'auth'
* ['auth.loading']
*/
excludeKeys?: string[];
excludeKeys?: string[]
/**
* Provide the storage type to sync the feature state to,
* it can be any storage which implements the 'Storage' interface.
*
* It will override the storage property in StorageSyncOptions
* @see IStorageSyncOptions
*/
storageForFeature?: Storage;
storageForFeature?: Storage
/**
* Sync to storage will only occur when this function returns true
* @param featureState the next feature state
* @param state the next state
*/
shouldSync?: (featureState: T[keyof T], state: T) => boolean;
shouldSync?: (featureState: T[keyof T], state: T) => boolean
/**
* Serializer for storage keys (feature state),
* it will override the storageKeySerializer in StorageSyncOptions
* @see IStorageSyncOptions
*
* @param key the storage item key
*/
storageKeySerializerForFeature?: (key: string) => string;
storageKeySerializerForFeature?: (key: string) => string
/**
* Serializer for the feature state (before saving to a storage location)
* @param featureState the next feature state
*/
serialize?: (featureState: T[keyof T]) => string;
serialize?: (featureState: T[keyof T]) => string
/**
* Deserializer for the feature state (after getting the state from a storage location)
*
* ISO Date objects which are stored as a string gets revived as Date object by default.
* @param featureState the feature state retrieved from a storage location
*/
deserialize?: (featureState: string) => T[keyof T];
deserialize?: (featureState: string) => T[keyof T]
}
14 changes: 7 additions & 7 deletions projects/ngrx-store-storagesync/src/lib/mock-storage.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
export class MockStorage implements Storage {
data = Object();
data = Object()

public get length(): number {
return Object.keys(this.data).length;
return Object.keys(this.data).length
}

public clear(): void {
for (const key of Object.keys(this.data)) {
delete this.data[key];
delete this.data[key]
}
}

public getItem(key: string): string {
return this.data[key] || null;
return this.data[key] || null
}

public key(index: number): string {
return Object.keys(this.data)[index];
return Object.keys(this.data)[index]
}

public removeItem(key: string): void {
delete this.data[key];
delete this.data[key]
}

public setItem(key: string, data: string): void {
this.data[key] = data;
this.data[key] = data
}
}
Loading

0 comments on commit f17cd40

Please sign in to comment.