@angular-architects/ngrx-toolkit 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,369 +1,404 @@
1
- # NgRx Toolkit
2
-
3
- [![npm](https://img.shields.io/npm/v/%40angular-architects%2Fngrx-toolkit.svg)](https://www.npmjs.com/package/%40angular-architects%2Fngrx-toolkit)
4
-
5
- <p align="center">
6
- <img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
7
- </p>
8
-
9
- NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
10
-
11
- - Devtools: Integration into Redux Devtools
12
- - Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
13
- - Redux Connector: Map NgRx Store Actions to a present Signal Store
14
-
15
- To install it, run
16
-
17
- ```shell
18
- npm i @angular-architects/ngrx-toolkit
19
- ```
20
-
21
-
22
- - [NgRx Toolkit](#ngrx-toolkit)
23
- - [Devtools: `withDevtools()`](#devtools-withdevtools)
24
- - [Redux: `withRedux()`](#redux-withredux)
25
- - [DataService `withDataService()`](#dataservice-withdataservice)
26
- - [DataService with Dynamic Properties](#dataservice-with-dynamic-properties)
27
- - [Redux Connector for the NgRx Signal Store `createReduxState()`](#redux-connector-for-the-ngrx-signal-store-createreduxstate)
28
- - [Use a present Signal Store](#use-a-present-signal-store)
29
- - [Use well-known NgRx Store Actions](#use-well-known-ngrx-store-actions)
30
- - [Map Actions to Methods](#map-actions-to-methods)
31
- - [Register an Angular Dependency Injection Provider](#register-an-angular-dependency-injection-provider)
32
- - [Use the Store in your Component](#use-the-store-in-your-component)
33
-
34
-
35
- ## Devtools: `withDevtools()`
36
-
37
- This extension is very easy to use. Just add it to a `signalStore`. Example:
38
-
39
- ```typescript
40
- export const FlightStore = signalStore(
41
- { providedIn: 'root' },
42
- withDevtools('flights'), // <-- add this
43
- withState({ flights: [] as Flight[] }),
44
- // ...
45
- );
46
- ```
47
-
48
- ## Redux: `withRedux()`
49
-
50
- `withRedux()` bring back the Redux pattern into the Signal Store.
51
-
52
- It can be combined with any other extension of the Signal Store.
53
-
54
- Example:
55
-
56
- ```typescript
57
- export const FlightStore = signalStore(
58
- { providedIn: 'root' },
59
- withState({ flights: [] as Flight[] }),
60
- withRedux({
61
- actions: {
62
- public: {
63
- load: payload<{ from: string; to: string }>(),
64
- },
65
- private: {
66
- loaded: payload<{ flights: Flight[] }>(),
67
- },
68
- },
69
- reducer(actions, on) {
70
- on(actions.loaded, ({ flights }, state) => {
71
- patchState(state, 'flights loaded', { flights });
72
- });
73
- },
74
- effects(actions, create) {
75
- const httpClient = inject(HttpClient);
76
- return {
77
- load$: create(actions.load).pipe(
78
- 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
- ),
85
- ),
86
- tap((flights) => actions.loaded({ flights })),
87
- ),
88
- };
89
- },
90
- }),
91
- );
92
- ```
93
-
94
- ## DataService `withDataService()`
95
-
96
- `withDataService()` allows to connect a Data Service to the store:
97
-
98
- This gives you a store for a CRUD use case:
99
-
100
- ```typescript
101
- export const SimpleFlightBookingStore = signalStore(
102
- { providedIn: 'root' },
103
- withCallState(),
104
- withEntities<Flight>(),
105
- withDataService({
106
- dataServiceType: FlightService,
107
- filter: { from: 'Paris', to: 'New York' },
108
- }),
109
- withUndoRedo(),
110
- );
111
- ```
112
-
113
- The features ``withCallState`` and ``withUndoRedo`` are optional, but when present, they enrich each other.
114
-
115
- The Data Service needs to implement the ``DataService`` interface:
116
-
117
- ```typescript
118
- @Injectable({
119
- providedIn: 'root'
120
- })
121
- export class FlightService implements DataService<Flight, FlightFilter> {
122
- loadById(id: EntityId): Promise<Flight> { ... }
123
- load(filter: FlightFilter): Promise<Flight[]> { ... }
124
-
125
- create(entity: Flight): Promise<Flight> { ... }
126
- update(entity: Flight): Promise<Flight> { ... }
127
- delete(entity: Flight): Promise<void> { ... }
128
- [...]
129
- }
130
- ```
131
-
132
- Once the store is defined, it gives its consumers numerous signals and methods they just need to delegate to:
133
-
134
- ```typescript
135
- @Component(...)
136
- export class FlightSearchSimpleComponent {
137
- private store = inject(SimpleFlightBookingStore);
138
-
139
- from = this.store.filter.from;
140
- to = this.store.filter.to;
141
- flights = this.store.entities;
142
- selected = this.store.selectedEntities;
143
- selectedIds = this.store.selectedIds;
144
-
145
- loading = this.store.loading;
146
-
147
- canUndo = this.store.canUndo;
148
- canRedo = this.store.canRedo;
149
-
150
- async search() {
151
- this.store.load();
152
- }
153
-
154
- undo(): void {
155
- this.store.undo();
156
- }
157
-
158
- redo(): void {
159
- this.store.redo();
160
- }
161
-
162
- updateCriteria(from: string, to: string): void {
163
- this.store.updateFilter({ from, to });
164
- }
165
-
166
- updateBasket(id: number, selected: boolean): void {
167
- this.store.updateSelected(id, selected);
168
- }
169
-
170
- }
171
- ```
172
-
173
- ## DataService with Dynamic Properties
174
-
175
- To avoid naming conflicts, the properties set up by ``withDataService`` and the connected features can be configured in a typesafe way:
176
-
177
- ```typescript
178
- export const FlightBookingStore = signalStore(
179
- { providedIn: 'root' },
180
- withCallState({
181
- collection: 'flight'
182
- }),
183
- withEntities({
184
- entity: type<Flight>(),
185
- collection: 'flight'
186
- }),
187
- withDataService({
188
- dataServiceType: FlightService,
189
- filter: { from: 'Graz', to: 'Hamburg' },
190
- collection: 'flight'
191
- }),
192
- withUndoRedo({
193
- collections: ['flight'],
194
- }),
195
- );
196
- ```
197
-
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:
199
-
200
- ```typescript
201
- @Component(...)
202
- export class FlightSearchDynamicComponent {
203
- private store = inject(FlightBookingStore);
204
-
205
- from = this.store.flightFilter.from;
206
- to = this.store.flightFilter.to;
207
- flights = this.store.flightEntities;
208
- selected = this.store.selectedFlightEntities;
209
- selectedIds = this.store.selectedFlightIds;
210
-
211
- loading = this.store.flightLoading;
212
-
213
- canUndo = this.store.canUndo;
214
- canRedo = this.store.canRedo;
215
-
216
- async search() {
217
- this.store.loadFlightEntities();
218
- }
219
-
220
- undo(): void {
221
- this.store.undo();
222
- }
223
-
224
- redo(): void {
225
- this.store.redo();
226
- }
227
-
228
- updateCriteria(from: string, to: string): void {
229
- this.store.updateFlightFilter({ from, to });
230
- }
231
-
232
- updateBasket(id: number, selected: boolean): void {
233
- this.store.updateSelectedFlightEntities(id, selected);
234
- }
235
-
236
- }
237
- ```
238
-
239
- ## Redux Connector for the NgRx Signal Store `createReduxState()`
240
-
241
- The Redux Connector turns any `signalStore()` into a Gobal State Management Slice following the Redux pattern.
242
-
243
- It supports:
244
-
245
- ✅ Well-known NgRx Store Actions \
246
- Global Action `dispatch()` \
247
- ✅ Angular Lazy Loading \
248
- Auto-generated `provideNamedStore()` & `injectNamedStore()` Functions \
249
- Global Action to Store Method Mappers \
250
-
251
-
252
- ### Use a present Signal Store
253
-
254
- ```typescript
255
- export const FlightStore = signalStore(
256
- // State
257
- withEntities({ entity: type<Flight>(), collection: 'flight' }),
258
- withEntities({ entity: type<number>(), collection: 'hide' }),
259
- // Selectors
260
- withComputed(({ flightEntities, hideEntities }) => ({
261
- filteredFlights: computed(() => flightEntities()
262
- .filter(flight => !hideEntities().includes(flight.id))),
263
- flightCount: computed(() => flightEntities().length),
264
- })),
265
- // Updater
266
- withMethods(store => ({
267
- setFlights: (state: { flights: Flight[] }) => patchState(store,
268
- setAllEntities(state.flights, { collection: 'flight' })),
269
- updateFlight: (state: { flight: Flight }) => patchState(store,
270
- updateEntity({ id: state.flight.id, changes: state.flight }, { collection: 'flight' })),
271
- clearFlights: () => patchState(store,
272
- removeAllEntities({ collection: 'flight' })),
273
- })),
274
- // Effects
275
- withMethods((store, flightService = inject(FlightService)) => ({
276
- loadFlights: reduxMethod<FlightFilter, { flights: Flight[] }>(pipe(
277
- switchMap(filter => from(
278
- flightService.load({ from: filter.from, to: filter.to })
279
- )),
280
- map(flights => ({ flights })),
281
- ), store.setFlights),
282
- })),
283
- );
284
- ```
285
-
286
- ### Use well-known NgRx Store Actions
287
-
288
- ```typescript
289
- export const ticketActions = createActionGroup({
290
- source: 'tickets',
291
- events: {
292
- 'flights load': props<FlightFilter>(),
293
- 'flights loaded': props<{ flights: Flight[] }>(),
294
- 'flights loaded by passenger': props<{ flights: Flight[] }>(),
295
- 'flight update': props<{ flight: Flight }>(),
296
- 'flights clear': emptyProps()
297
- }
298
- });
299
- ```
300
-
301
- ### Map Actions to Methods
302
-
303
- ```typescript
304
- export const { provideFlightStore, injectFlightStore } =
305
- createReduxState('flight', FlightStore, store => withActionMappers(
306
- mapAction(
307
- // Filtered Action
308
- ticketActions.flightsLoad,
309
- // Side-Effect
310
- store.loadFlights,
311
- // Result Action
312
- ticketActions.flightsLoaded),
313
- mapAction(
314
- // Filtered Actions
315
- ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger,
316
- // State Updater Method (like Reducers)
317
- store.setFlights
318
- ),
319
- mapAction(ticketActions.flightUpdate, store.updateFlight),
320
- mapAction(ticketActions.flightsClear, store.clearFlights),
321
- )
322
- );
323
- ```
324
-
325
- ### Register an Angular Dependency Injection Provider
326
-
327
- ```typescript
328
- export const appRoutes: Route[] = [
329
- {
330
- path: 'flight-search-redux-connector',
331
- providers: [provideFlightStore()],
332
- component: FlightSearchReducConnectorComponent
333
- },
334
- ];
335
- ```
336
-
337
- ### Use the Store in your Component
338
-
339
- ```typescript
340
- @Component({
341
- standalone: true,
342
- imports: [
343
- JsonPipe,
344
- RouterLink,
345
- FormsModule,
346
- FlightCardComponent
347
- ],
348
- selector: 'demo-flight-search-redux-connector',
349
- templateUrl: './flight-search.component.html',
350
- })
351
- export class FlightSearchReducConnectorComponent {
352
- private store = injectFlightStore();
353
-
354
- protected flights = this.store.flightEntities;
355
-
356
- protected search() {
357
- this.store.dispatch(
358
- ticketActions.flightsLoad({
359
- from: this.localState.filter.from(),
360
- to: this.localState.filter.to()
361
- })
362
- );
363
- }
364
-
365
- protected reset(): void {
366
- this.store.dispatch(ticketActions.flightsClear());
367
- }
368
- }
369
- ```
1
+ # NgRx Toolkit
2
+
3
+ [![npm](https://img.shields.io/npm/v/%40angular-architects%2Fngrx-toolkit.svg)](https://www.npmjs.com/package/%40angular-architects%2Fngrx-toolkit)
4
+
5
+ <p align="center">
6
+ <img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
7
+ </p>
8
+
9
+ NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
10
+
11
+ - Devtools: Integration into Redux Devtools
12
+ - Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
13
+ - Redux Connector: Map NgRx Store Actions to a present Signal Store
14
+
15
+ To install it, run
16
+
17
+ ```shell
18
+ npm i @angular-architects/ngrx-toolkit
19
+ ```
20
+
21
+
22
+ - [NgRx Toolkit](#ngrx-toolkit)
23
+ - [Devtools: `withDevtools()`](#devtools-withdevtools)
24
+ - [Redux: `withRedux()`](#redux-withredux)
25
+ - [DataService `withDataService()`](#dataservice-withdataservice)
26
+ - [DataService with Dynamic Properties](#dataservice-with-dynamic-properties)
27
+ - [Redux Connector for the NgRx Signal Store `createReduxState()`](#redux-connector-for-the-ngrx-signal-store-createreduxstate)
28
+ - [Use a present Signal Store](#use-a-present-signal-store)
29
+ - [Use well-known NgRx Store Actions](#use-well-known-ngrx-store-actions)
30
+ - [Map Actions to Methods](#map-actions-to-methods)
31
+ - [Register an Angular Dependency Injection Provider](#register-an-angular-dependency-injection-provider)
32
+ - [Use the Store in your Component](#use-the-store-in-your-component)
33
+
34
+
35
+ ## Devtools: `withDevtools()`
36
+
37
+ This extension is very easy to use. Just add it to a `signalStore`. Example:
38
+
39
+ ```typescript
40
+ export const FlightStore = signalStore(
41
+ { providedIn: 'root' },
42
+ withDevtools('flights'), // <-- add this
43
+ withState({ flights: [] as Flight[] })
44
+ // ...
45
+ );
46
+ ```
47
+
48
+ ## Redux: `withRedux()`
49
+
50
+ `withRedux()` bring back the Redux pattern into the Signal Store.
51
+
52
+ It can be combined with any other extension of the Signal Store.
53
+
54
+ Example:
55
+
56
+ ```typescript
57
+ export const FlightStore = signalStore(
58
+ { providedIn: 'root' },
59
+ withState({ flights: [] as Flight[] }),
60
+ withRedux({
61
+ actions: {
62
+ public: {
63
+ load: payload<{ from: string; to: string }>(),
64
+ },
65
+ private: {
66
+ loaded: payload<{ flights: Flight[] }>(),
67
+ },
68
+ },
69
+ reducer(actions, on) {
70
+ on(actions.loaded, ({ flights }, state) => {
71
+ patchState(state, 'flights loaded', { flights });
72
+ });
73
+ },
74
+ effects(actions, create) {
75
+ const httpClient = inject(HttpClient);
76
+ return {
77
+ load$: create(actions.load).pipe(
78
+ switchMap(({ from, to }) =>
79
+ httpClient.get<Flight[]>('https://demo.angulararchitects.io/api/flight', {
80
+ params: new HttpParams().set('from', from).set('to', to),
81
+ })
82
+ ),
83
+ tap((flights) => actions.loaded({ flights }))
84
+ ),
85
+ };
86
+ },
87
+ })
88
+ );
89
+ ```
90
+
91
+ ## DataService `withDataService()`
92
+
93
+ `withDataService()` allows to connect a Data Service to the store:
94
+
95
+ This gives you a store for a CRUD use case:
96
+
97
+ ```typescript
98
+ export const SimpleFlightBookingStore = signalStore(
99
+ { providedIn: 'root' },
100
+ withCallState(),
101
+ withEntities<Flight>(),
102
+ withDataService({
103
+ dataServiceType: FlightService,
104
+ filter: { from: 'Paris', to: 'New York' },
105
+ }),
106
+ withUndoRedo()
107
+ );
108
+ ```
109
+
110
+ The features `withCallState` and `withUndoRedo` are optional, but when present, they enrich each other.
111
+
112
+ The Data Service needs to implement the `DataService` interface:
113
+
114
+ ```typescript
115
+ @Injectable({
116
+ providedIn: 'root'
117
+ })
118
+ export class FlightService implements DataService<Flight, FlightFilter> {
119
+ loadById(id: EntityId): Promise<Flight> { ... }
120
+ load(filter: FlightFilter): Promise<Flight[]> { ... }
121
+
122
+ create(entity: Flight): Promise<Flight> { ... }
123
+ update(entity: Flight): Promise<Flight> { ... }
124
+ delete(entity: Flight): Promise<void> { ... }
125
+ [...]
126
+ }
127
+ ```
128
+
129
+ Once the store is defined, it gives its consumers numerous signals and methods they just need to delegate to:
130
+
131
+ ```typescript
132
+ @Component(...)
133
+ export class FlightSearchSimpleComponent {
134
+ private store = inject(SimpleFlightBookingStore);
135
+
136
+ from = this.store.filter.from;
137
+ to = this.store.filter.to;
138
+ flights = this.store.entities;
139
+ selected = this.store.selectedEntities;
140
+ selectedIds = this.store.selectedIds;
141
+
142
+ loading = this.store.loading;
143
+
144
+ canUndo = this.store.canUndo;
145
+ canRedo = this.store.canRedo;
146
+
147
+ async search() {
148
+ this.store.load();
149
+ }
150
+
151
+ undo(): void {
152
+ this.store.undo();
153
+ }
154
+
155
+ redo(): void {
156
+ this.store.redo();
157
+ }
158
+
159
+ updateCriteria(from: string, to: string): void {
160
+ this.store.updateFilter({ from, to });
161
+ }
162
+
163
+ updateBasket(id: number, selected: boolean): void {
164
+ this.store.updateSelected(id, selected);
165
+ }
166
+
167
+ }
168
+ ```
169
+
170
+ ## DataService with Dynamic Properties
171
+
172
+ To avoid naming conflicts, the properties set up by `withDataService` and the connected features can be configured in a typesafe way:
173
+
174
+ ```typescript
175
+ export const FlightBookingStore = signalStore(
176
+ { providedIn: 'root' },
177
+ withCallState({
178
+ collection: 'flight',
179
+ }),
180
+ withEntities({
181
+ entity: type<Flight>(),
182
+ collection: 'flight',
183
+ }),
184
+ withDataService({
185
+ dataServiceType: FlightService,
186
+ filter: { from: 'Graz', to: 'Hamburg' },
187
+ collection: 'flight',
188
+ }),
189
+ withUndoRedo({
190
+ collections: ['flight'],
191
+ })
192
+ );
193
+ ```
194
+
195
+ 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:
196
+
197
+ ```typescript
198
+ @Component(...)
199
+ export class FlightSearchDynamicComponent {
200
+ private store = inject(FlightBookingStore);
201
+
202
+ from = this.store.flightFilter.from;
203
+ to = this.store.flightFilter.to;
204
+ flights = this.store.flightEntities;
205
+ selected = this.store.selectedFlightEntities;
206
+ selectedIds = this.store.selectedFlightIds;
207
+
208
+ loading = this.store.flightLoading;
209
+
210
+ canUndo = this.store.canUndo;
211
+ canRedo = this.store.canRedo;
212
+
213
+ async search() {
214
+ this.store.loadFlightEntities();
215
+ }
216
+
217
+ undo(): void {
218
+ this.store.undo();
219
+ }
220
+
221
+ redo(): void {
222
+ this.store.redo();
223
+ }
224
+
225
+ updateCriteria(from: string, to: string): void {
226
+ this.store.updateFlightFilter({ from, to });
227
+ }
228
+
229
+ updateBasket(id: number, selected: boolean): void {
230
+ this.store.updateSelectedFlightEntities(id, selected);
231
+ }
232
+
233
+ }
234
+ ```
235
+
236
+ ## Storage Sync `withStorageSync()`
237
+
238
+ `withStorageSync` adds automatic or manual synchronization with Web Storage (`localstorage`/`sessionstorage`).
239
+
240
+ > [!WARNING]
241
+ > As Web Storage only works in browser environments it will fallback to a stub implementation on server environments.
242
+
243
+ Example:
244
+
245
+ ```ts
246
+ const SyncStore = signalStore(
247
+ withStorageSync<User>({
248
+ key: 'synced', // key used when writing to/reading from storage
249
+ autoSync: false, // read from storage on init and write on state changes - `true` by default
250
+ select: (state: User) => Partial<User>, // projection to keep specific slices in sync
251
+ parse: (stateString: string) => State, // custom parsing from storage - `JSON.parse` by default
252
+ stringify: (state: User) => string, // custom stringification - `JSON.stringify` by default
253
+ storage: () => sessionstorage, // factory to select storage to sync with
254
+ })
255
+ );
256
+ ```
257
+
258
+ ```ts
259
+ @Component(...)
260
+ public class SyncedStoreComponent {
261
+ private syncStore = inject(SyncStore);
262
+
263
+ updateFromStorage(): void {
264
+ this.syncStore.readFromStorage(); // reads the stored item from storage and patches the state
265
+ }
266
+
267
+ updateStorage(): void {
268
+ this.syncStore.writeToStorage(); // writes the current state to storage
269
+ }
270
+
271
+ clearStorage(): void {
272
+ this.syncStore.clearStorage(); // clears the stored item in storage
273
+
274
+ ## Redux Connector for the NgRx Signal Store `createReduxState()`
275
+
276
+ The Redux Connector turns any `signalStore()` into a Gobal State Management Slice following the Redux pattern.
277
+
278
+ It supports:
279
+
280
+ Well-known NgRx Store Actions \
281
+ ✅ Global Action `dispatch()` \
282
+ ✅ Angular Lazy Loading \
283
+ ✅ Auto-generated `provideNamedStore()` & `injectNamedStore()` Functions \
284
+ ✅ Global Action to Store Method Mappers \
285
+
286
+
287
+ ### Use a present Signal Store
288
+
289
+ ```typescript
290
+ export const FlightStore = signalStore(
291
+ // State
292
+ withEntities({ entity: type<Flight>(), collection: 'flight' }),
293
+ withEntities({ entity: type<number>(), collection: 'hide' }),
294
+ // Selectors
295
+ withComputed(({ flightEntities, hideEntities }) => ({
296
+ filteredFlights: computed(() => flightEntities()
297
+ .filter(flight => !hideEntities().includes(flight.id))),
298
+ flightCount: computed(() => flightEntities().length),
299
+ })),
300
+ // Updater
301
+ withMethods(store => ({
302
+ setFlights: (state: { flights: Flight[] }) => patchState(store,
303
+ setAllEntities(state.flights, { collection: 'flight' })),
304
+ updateFlight: (state: { flight: Flight }) => patchState(store,
305
+ updateEntity({ id: state.flight.id, changes: state.flight }, { collection: 'flight' })),
306
+ clearFlights: () => patchState(store,
307
+ removeAllEntities({ collection: 'flight' })),
308
+ })),
309
+ // Effects
310
+ withMethods((store, flightService = inject(FlightService)) => ({
311
+ loadFlights: reduxMethod<FlightFilter, { flights: Flight[] }>(pipe(
312
+ switchMap(filter => from(
313
+ flightService.load({ from: filter.from, to: filter.to })
314
+ )),
315
+ map(flights => ({ flights })),
316
+ ), store.setFlights),
317
+ })),
318
+ );
319
+ ```
320
+
321
+ ### Use well-known NgRx Store Actions
322
+
323
+ ```typescript
324
+ export const ticketActions = createActionGroup({
325
+ source: 'tickets',
326
+ events: {
327
+ 'flights load': props<FlightFilter>(),
328
+ 'flights loaded': props<{ flights: Flight[] }>(),
329
+ 'flights loaded by passenger': props<{ flights: Flight[] }>(),
330
+ 'flight update': props<{ flight: Flight }>(),
331
+ 'flights clear': emptyProps()
332
+ }
333
+ });
334
+ ```
335
+
336
+ ### Map Actions to Methods
337
+
338
+ ```typescript
339
+ export const { provideFlightStore, injectFlightStore } =
340
+ createReduxState('flight', FlightStore, store => withActionMappers(
341
+ mapAction(
342
+ // Filtered Action
343
+ ticketActions.flightsLoad,
344
+ // Side-Effect
345
+ store.loadFlights,
346
+ // Result Action
347
+ ticketActions.flightsLoaded),
348
+ mapAction(
349
+ // Filtered Actions
350
+ ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger,
351
+ // State Updater Method (like Reducers)
352
+ store.setFlights
353
+ ),
354
+ mapAction(ticketActions.flightUpdate, store.updateFlight),
355
+ mapAction(ticketActions.flightsClear, store.clearFlights),
356
+ )
357
+ );
358
+ ```
359
+
360
+ ### Register an Angular Dependency Injection Provider
361
+
362
+ ```typescript
363
+ export const appRoutes: Route[] = [
364
+ {
365
+ path: 'flight-search-redux-connector',
366
+ providers: [provideFlightStore()],
367
+ component: FlightSearchReducConnectorComponent
368
+ },
369
+ ];
370
+ ```
371
+
372
+ ### Use the Store in your Component
373
+
374
+ ```typescript
375
+ @Component({
376
+ standalone: true,
377
+ imports: [
378
+ JsonPipe,
379
+ RouterLink,
380
+ FormsModule,
381
+ FlightCardComponent
382
+ ],
383
+ selector: 'demo-flight-search-redux-connector',
384
+ templateUrl: './flight-search.component.html',
385
+ })
386
+ export class FlightSearchReducConnectorComponent {
387
+ private store = injectFlightStore();
388
+
389
+ protected flights = this.store.flightEntities;
390
+
391
+ protected search() {
392
+ this.store.dispatch(
393
+ ticketActions.flightsLoad({
394
+ from: this.localState.filter.from(),
395
+ to: this.localState.filter.to()
396
+ })
397
+ );
398
+ }
399
+
400
+ protected reset(): void {
401
+ this.store.dispatch(ticketActions.flightsClear());
402
+ }
403
+ }
404
+ ```