Skip to content

Commit e5606d8

Browse files
docs: add faq, syncStorage and explain dependency strictness
1 parent 6a2abf2 commit e5606d8

File tree

2 files changed

+143
-25
lines changed

2 files changed

+143
-25
lines changed

README.md

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
<img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
77
</p>
88

9-
NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
9+
NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers features, like:
1010

1111
- Devtools: Integration into Redux Devtools
1212
- Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
13+
- Storage Sync: Synchronize the Store with Web Storage
1314
- Redux Connector: Map NgRx Store Actions to a present Signal Store
1415

1516
To install it, run
@@ -18,18 +19,36 @@ To install it, run
1819
npm i @angular-architects/ngrx-toolkit
1920
```
2021

22+
Starting with 18.0.0-rc.2, we have a [strict version dependency](#why-is-the-version-range-to-the-ngrxsignals-dependency-so-strict) to `@ngrx/signals`:
23+
24+
| @ngrx/signals | @angular-architects/ngrx-toolkit |
25+
|----------------|----------------------------------|
26+
| <= 18.0.0-rc.1 | 0.0.4 |
27+
| 18.0.0-rc.2 | 18.0.0-rc.2.x |
28+
29+
To install it, run
30+
31+
```shell
32+
npm i @angular-architects/ngrx-toolkit
33+
```
34+
2135

2236
- [NgRx Toolkit](#ngrx-toolkit)
2337
- [Devtools: `withDevtools()`](#devtools-withdevtools)
2438
- [Redux: `withRedux()`](#redux-withredux)
2539
- [DataService `withDataService()`](#dataservice-withdataservice)
2640
- [DataService with Dynamic Properties](#dataservice-with-dynamic-properties)
41+
- [Storage Sync `withStorageSync`](#storage-sync-withstoragesync)
2742
- [Redux Connector for the NgRx Signal Store `createReduxState()`](#redux-connector-for-the-ngrx-signal-store-createreduxstate)
2843
- [Use a present Signal Store](#use-a-present-signal-store)
2944
- [Use well-known NgRx Store Actions](#use-well-known-ngrx-store-actions)
3045
- [Map Actions to Methods](#map-actions-to-methods)
3146
- [Register an Angular Dependency Injection Provider](#register-an-angular-dependency-injection-provider)
3247
- [Use the Store in your Component](#use-the-store-in-your-component)
48+
- [FAQ](#faq)
49+
- [Why is the version range to the `@ngrx/signals` dependency so strict?](#why-is-the-version-range-to-the-ngrxsignals-dependency-so-strict)
50+
- [I have an idea for a new extension, can I contribute?](#i-have-an-idea-for-a-new-extension-can-i-contribute)
51+
- [I require a feature that is not available in a lower version. What should I do?](#i-require-a-feature-that-is-not-available-in-a-lower-version-what-should-i-do)
3352

3453

3554
## Devtools: `withDevtools()`
@@ -40,7 +59,7 @@ This extension is very easy to use. Just add it to a `signalStore`. Example:
4059
export const FlightStore = signalStore(
4160
{ providedIn: 'root' },
4261
withDevtools('flights'), // <-- add this
43-
withState({ flights: [] as Flight[] }),
62+
withState({ flights: [] as Flight[] })
4463
// ...
4564
);
4665
```
@@ -76,18 +95,15 @@ export const FlightStore = signalStore(
7695
return {
7796
load$: create(actions.load).pipe(
7897
switchMap(({ from, to }) =>
79-
httpClient.get<Flight[]>(
80-
'https://demo.angulararchitects.io/api/flight',
81-
{
82-
params: new HttpParams().set('from', from).set('to', to),
83-
},
84-
),
98+
httpClient.get<Flight[]>('https://demo.angulararchitects.io/api/flight', {
99+
params: new HttpParams().set('from', from).set('to', to),
100+
})
85101
),
86-
tap((flights) => actions.loaded({ flights })),
102+
tap((flights) => actions.loaded({ flights }))
87103
),
88104
};
89105
},
90-
}),
106+
})
91107
);
92108
```
93109

@@ -103,18 +119,18 @@ export const SimpleFlightBookingStore = signalStore(
103119
withCallState(),
104120
withEntities<Flight>(),
105121
withDataService({
106-
dataServiceType: FlightService,
122+
dataServiceType: FlightService,
107123
filter: { from: 'Paris', to: 'New York' },
108124
}),
109-
withUndoRedo(),
125+
withUndoRedo()
110126
);
111127
```
112128

113-
The features ``withCallState`` and ``withUndoRedo`` are optional, but when present, they enrich each other.
129+
The features `withCallState` and `withUndoRedo` are optional, but when present, they enrich each other.
114130

115-
The Data Service needs to implement the ``DataService`` interface:
131+
The Data Service needs to implement the `DataService` interface:
116132

117-
```typescript
133+
```typescript
118134
@Injectable({
119135
providedIn: 'root'
120136
})
@@ -172,30 +188,30 @@ export class FlightSearchSimpleComponent {
172188

173189
## DataService with Dynamic Properties
174190

175-
To avoid naming conflicts, the properties set up by ``withDataService`` and the connected features can be configured in a typesafe way:
191+
To avoid naming conflicts, the properties set up by `withDataService` and the connected features can be configured in a typesafe way:
176192

177193
```typescript
178194
export const FlightBookingStore = signalStore(
179195
{ providedIn: 'root' },
180196
withCallState({
181-
collection: 'flight'
197+
collection: 'flight',
182198
}),
183-
withEntities({
184-
entity: type<Flight>(),
185-
collection: 'flight'
199+
withEntities({
200+
entity: type<Flight>(),
201+
collection: 'flight',
186202
}),
187203
withDataService({
188-
dataServiceType: FlightService,
204+
dataServiceType: FlightService,
189205
filter: { from: 'Graz', to: 'Hamburg' },
190-
collection: 'flight'
206+
collection: 'flight',
191207
}),
192208
withUndoRedo({
193209
collections: ['flight'],
194-
}),
210+
})
195211
);
196212
```
197213

198-
This setup makes them use ``flight`` as part of the used property names. As these implementations respect the Type Script type system, the compiler will make sure these properties are used in a typesafe way:
214+
This setup makes them use `flight` as part of the used property names. As these implementations respect the Type Script type system, the compiler will make sure these properties are used in a typesafe way:
199215

200216
```typescript
201217
@Component(...)
@@ -236,6 +252,47 @@ export class FlightSearchDynamicComponent {
236252
}
237253
```
238254

255+
## Storage Sync `withStorageSync()`
256+
257+
`withStorageSync` adds automatic or manual synchronization with Web Storage (`localstorage`/`sessionstorage`).
258+
259+
> [!WARNING]
260+
> As Web Storage only works in browser environments it will fallback to a stub implementation on server environments.
261+
262+
Example:
263+
264+
```ts
265+
const SyncStore = signalStore(
266+
withStorageSync<User>({
267+
key: 'synced', // key used when writing to/reading from storage
268+
autoSync: false, // read from storage on init and write on state changes - `true` by default
269+
select: (state: User) => Partial<User>, // projection to keep specific slices in sync
270+
parse: (stateString: string) => State, // custom parsing from storage - `JSON.parse` by default
271+
stringify: (state: User) => string, // custom stringification - `JSON.stringify` by default
272+
storage: () => sessionstorage, // factory to select storage to sync with
273+
})
274+
);
275+
```
276+
277+
```ts
278+
@Component(...)
279+
public class SyncedStoreComponent {
280+
private syncStore = inject(SyncStore);
281+
282+
updateFromStorage(): void {
283+
this.syncStore.readFromStorage(); // reads the stored item from storage and patches the state
284+
}
285+
286+
updateStorage(): void {
287+
this.syncStore.writeToStorage(); // writes the current state to storage
288+
}
289+
290+
clearStorage(): void {
291+
this.syncStore.clearStorage(); // clears the stored item in storage
292+
}
293+
}
294+
```
295+
239296
## Redux Connector for the NgRx Signal Store `createReduxState()`
240297

241298
The Redux Connector turns any `signalStore()` into a Gobal State Management Slice following the Redux pattern.
@@ -367,3 +424,24 @@ export class FlightSearchReducConnectorComponent {
367424
}
368425
}
369426
```
427+
## FAQ
428+
429+
### Why is the version range to the `@ngrx/signals` dependency so strict?
430+
431+
The strict version range for @ngrx/signals is necessary because some of our features rely on encapsulated types, which can change even in a patch release.
432+
433+
To ensure stability, we clone these internal types and run integration tests for each release. This rigorous testing means we may need to update our version, even for a patch release, to maintain compatibility and stability.
434+
435+
### I have an idea for a new extension, can I contribute?
436+
437+
Yes, please! We are always looking for new ideas and contributions.
438+
439+
Since we don't want to bloat the library, we are very selective about new features. You also have to provide the following:
440+
- Good test coverage so that we can update it properly and don't have to call you 😉.
441+
- A use case showing the feature in action in the demo app of the repository.
442+
- An entry to the README.md.
443+
444+
### I require a feature that is not available in a lower version. What should I do?
445+
446+
Please create an issue. Very likely, we are able to cherry-pick the feature into the lower version.
447+

libs/ngrx-toolkit/README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
<img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
77
</p>
88

9-
NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
9+
NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers features, like:
1010

1111
- Devtools: Integration into Redux Devtools
1212
- Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
13+
- Storage Sync: Synchronize the Store with Web Storage
1314
- Redux Connector: Map NgRx Store Actions to a present Signal Store
1415

1516
To install it, run
@@ -18,18 +19,36 @@ To install it, run
1819
npm i @angular-architects/ngrx-toolkit
1920
```
2021

22+
Starting with 18.0.0-rc.2, we have a [strict version dependency](#why-is-the-version-range-to-the-ngrxsignals-dependency-so-strict) to `@ngrx/signals`:
23+
24+
| @ngrx/signals | @angular-architects/ngrx-toolkit |
25+
|----------------|----------------------------------|
26+
| <= 18.0.0-rc.1 | 0.0.4 |
27+
| 18.0.0-rc.2 | 18.0.0-rc.2.x |
28+
29+
To install it, run
30+
31+
```shell
32+
npm i @angular-architects/ngrx-toolkit
33+
```
34+
2135

2236
- [NgRx Toolkit](#ngrx-toolkit)
2337
- [Devtools: `withDevtools()`](#devtools-withdevtools)
2438
- [Redux: `withRedux()`](#redux-withredux)
2539
- [DataService `withDataService()`](#dataservice-withdataservice)
2640
- [DataService with Dynamic Properties](#dataservice-with-dynamic-properties)
41+
- [Storage Sync `withStorageSync`](#storage-sync-withstoragesync)
2742
- [Redux Connector for the NgRx Signal Store `createReduxState()`](#redux-connector-for-the-ngrx-signal-store-createreduxstate)
2843
- [Use a present Signal Store](#use-a-present-signal-store)
2944
- [Use well-known NgRx Store Actions](#use-well-known-ngrx-store-actions)
3045
- [Map Actions to Methods](#map-actions-to-methods)
3146
- [Register an Angular Dependency Injection Provider](#register-an-angular-dependency-injection-provider)
3247
- [Use the Store in your Component](#use-the-store-in-your-component)
48+
- [FAQ](#faq)
49+
- [Why is the version range to the `@ngrx/signals` dependency so strict?](#why-is-the-version-range-to-the-ngrxsignals-dependency-so-strict)
50+
- [I have an idea for a new extension, can I contribute?](#i-have-an-idea-for-a-new-extension-can-i-contribute)
51+
- [I require a feature that is not available in a lower version. What should I do?](#i-require-a-feature-that-is-not-available-in-a-lower-version-what-should-i-do)
3352

3453

3554
## Devtools: `withDevtools()`
@@ -405,3 +424,24 @@ export class FlightSearchReducConnectorComponent {
405424
}
406425
}
407426
```
427+
## FAQ
428+
429+
### Why is the version range to the `@ngrx/signals` dependency so strict?
430+
431+
The strict version range for @ngrx/signals is necessary because some of our features rely on encapsulated types, which can change even in a patch release.
432+
433+
To ensure stability, we clone these internal types and run integration tests for each release. This rigorous testing means we may need to update our version, even for a patch release, to maintain compatibility and stability.
434+
435+
### I have an idea for a new extension, can I contribute?
436+
437+
Yes, please! We are always looking for new ideas and contributions.
438+
439+
Since we don't want to bloat the library, we are very selective about new features. You also have to provide the following:
440+
- Good test coverage so that we can update it properly and don't have to call you 😉.
441+
- A use case showing the feature in action in the demo app of the repository.
442+
- An entry to the README.md.
443+
444+
### I require a feature that is not available in a lower version. What should I do?
445+
446+
Please create an issue. Very likely, we are able to cherry-pick the feature into the lower version.
447+

0 commit comments

Comments
 (0)