@angular-architects/ngrx-toolkit 0.0.4 → 0.0.6
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 +369 -220
- package/esm2022/index.mjs +3 -1
- package/esm2022/lib/assertions/assertions.mjs +1 -1
- package/esm2022/lib/redux-connector/create-redux.mjs +41 -0
- package/esm2022/lib/redux-connector/index.mjs +2 -0
- package/esm2022/lib/redux-connector/model.mjs +2 -0
- package/esm2022/lib/redux-connector/rxjs-interop/index.mjs +2 -0
- package/esm2022/lib/redux-connector/rxjs-interop/redux-method.mjs +22 -0
- package/esm2022/lib/redux-connector/signal-redux-store.mjs +43 -0
- package/esm2022/lib/redux-connector/util.mjs +13 -0
- package/esm2022/lib/shared/empty.mjs +1 -1
- package/esm2022/lib/with-call-state.mjs +1 -1
- package/esm2022/lib/with-data-service.mjs +1 -1
- package/esm2022/lib/with-devtools.mjs +1 -1
- package/esm2022/lib/with-redux.mjs +1 -1
- package/esm2022/lib/with-undo-redo.mjs +1 -1
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +127 -17
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -1
- package/index.d.ts +2 -0
- package/lib/redux-connector/create-redux.d.ts +13 -0
- package/lib/redux-connector/index.d.ts +1 -0
- package/lib/redux-connector/model.d.ts +36 -0
- package/lib/redux-connector/rxjs-interop/index.d.ts +1 -0
- package/lib/redux-connector/rxjs-interop/redux-method.d.ts +11 -0
- package/lib/redux-connector/signal-redux-store.d.ts +11 -0
- package/lib/redux-connector/util.d.ts +5 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,220 +1,369 @@
|
|
|
1
|
-
# NgRx Toolkit
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Example:
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
export const FlightStore = signalStore(
|
|
41
|
-
{ providedIn: 'root' },
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
|
|
102
|
-
providedIn: 'root'
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
@
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
1
|
+
# NgRx Toolkit
|
|
2
|
+
|
|
3
|
+
[](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
|
+
```
|
package/esm2022/index.mjs
CHANGED
|
@@ -3,4 +3,6 @@ export * from './lib/with-redux';
|
|
|
3
3
|
export * from './lib/with-call-state';
|
|
4
4
|
export * from './lib/with-undo-redo';
|
|
5
5
|
export * from './lib/with-data-service';
|
|
6
|
-
|
|
6
|
+
export * from './lib/redux-connector';
|
|
7
|
+
export * from './lib/redux-connector/rxjs-interop';
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL25ncngtdG9vbGtpdC9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RSxjQUFjLGtCQUFrQixDQUFDO0FBRWpDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLHlCQUF5QixDQUFDO0FBRXhDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQ0FBb0MsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IHdpdGhEZXZ0b29scywgcGF0Y2hTdGF0ZSwgQWN0aW9uIH0gZnJvbSAnLi9saWIvd2l0aC1kZXZ0b29scyc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL3dpdGgtcmVkdXgnO1xyXG5cclxuZXhwb3J0ICogZnJvbSAnLi9saWIvd2l0aC1jYWxsLXN0YXRlJztcclxuZXhwb3J0ICogZnJvbSAnLi9saWIvd2l0aC11bmRvLXJlZG8nO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi93aXRoLWRhdGEtc2VydmljZSc7XHJcblxyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9yZWR1eC1jb25uZWN0b3InO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9yZWR1eC1jb25uZWN0b3Ivcnhqcy1pbnRlcm9wJztcclxuIl19
|
|
@@ -3,4 +3,4 @@ export function assertActionFnSpecs(obj) {
|
|
|
3
3
|
throw new Error('%o is not an Action Specification');
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXJ0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvbmdyeC10b29sa2l0L3NyYy9saWIvYXNzZXJ0aW9ucy9hc3NlcnRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsR0FBWTtJQUVaLElBQUksQ0FBQyxHQUFHLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFO1FBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLENBQUMsQ0FBQztLQUN0RDtBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBY3Rpb25zRm5TcGVjcyB9IGZyb20gJy4uL3dpdGgtcmVkdXgnO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydEFjdGlvbkZuU3BlY3MoXHJcbiAgb2JqOiB1bmtub3duXHJcbik6IGFzc2VydHMgb2JqIGlzIEFjdGlvbnNGblNwZWNzIHtcclxuICBpZiAoIW9iaiB8fCB0eXBlb2Ygb2JqICE9PSAnb2JqZWN0Jykge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKCclbyBpcyBub3QgYW4gQWN0aW9uIFNwZWNpZmljYXRpb24nKTtcclxuICB9XHJcbn1cclxuIl19
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ENVIRONMENT_INITIALIZER, inject, makeEnvironmentProviders } from "@angular/core";
|
|
2
|
+
import { SignalReduxStore, injectReduxDispatch } from "./signal-redux-store";
|
|
3
|
+
import { capitalize, isActionCreator } from "./util";
|
|
4
|
+
export function mapAction(...args) {
|
|
5
|
+
let resultMethod = args.pop();
|
|
6
|
+
let storeMethod = args.pop();
|
|
7
|
+
if (isActionCreator(storeMethod)) {
|
|
8
|
+
args.push(storeMethod);
|
|
9
|
+
storeMethod = resultMethod || storeMethod;
|
|
10
|
+
resultMethod = undefined;
|
|
11
|
+
}
|
|
12
|
+
const types = args.map((creator) => creator.type);
|
|
13
|
+
return {
|
|
14
|
+
types,
|
|
15
|
+
storeMethod,
|
|
16
|
+
resultMethod
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function withActionMappers(...mappers) {
|
|
20
|
+
return mappers;
|
|
21
|
+
}
|
|
22
|
+
export function createReduxState(storeName, signalStore, withActionMappers) {
|
|
23
|
+
const isRootProvider = signalStore?.ɵprov?.providedIn === 'root';
|
|
24
|
+
return {
|
|
25
|
+
[`provide${capitalize(storeName)}Store`]: (connectReduxDevtools = false) => makeEnvironmentProviders([
|
|
26
|
+
isRootProvider ? [] : signalStore,
|
|
27
|
+
{
|
|
28
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
29
|
+
multi: true,
|
|
30
|
+
useFactory: (signalReduxStore = inject(SignalReduxStore), store = inject(signalStore)) => () => {
|
|
31
|
+
if (connectReduxDevtools) {
|
|
32
|
+
// addStoreToReduxDevtools(store, storeName, false);
|
|
33
|
+
}
|
|
34
|
+
signalReduxStore.connectFeatureStore(withActionMappers(store));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]),
|
|
38
|
+
[`inject${capitalize(storeName)}Store`]: () => Object.assign(inject(signalStore), { dispatch: injectReduxDispatch() })
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXJlZHV4LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9uZ3J4LXRvb2xraXQvc3JjL2xpYi9yZWR1eC1jb25uZWN0b3IvY3JlYXRlLXJlZHV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHMUYsT0FBTyxFQUFFLGdCQUFnQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0UsT0FBTyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFxQnJELE1BQU0sVUFBVSxTQUFTLENBR3ZCLEdBQUcsSUFJRjtJQUVELElBQUksWUFBWSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQTJELENBQUM7SUFDdkYsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBa0UsQ0FBQztJQUU3RixJQUFJLGVBQWUsQ0FBQyxXQUFXLENBQUMsRUFBRTtRQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3ZCLFdBQVcsR0FBRyxZQUFZLElBQUksV0FBVyxDQUFDO1FBQzFDLFlBQVksR0FBRyxTQUFTLENBQUM7S0FDMUI7SUFFRCxNQUFNLEtBQUssR0FBSSxJQUE0QixDQUFDLEdBQUcsQ0FDN0MsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQ2lCLENBQUM7SUFFN0MsT0FBTztRQUNMLEtBQUs7UUFDTCxXQUFXO1FBQ1gsWUFBWTtLQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixHQUFHLE9BQWlEO0lBRXBELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxNQUFNLFVBQVUsZ0JBQWdCLENBSTlCLFNBQW9CLEVBQ3BCLFdBQWtCLEVBQ2xCLGlCQUEyRjtJQUUzRixNQUFNLGNBQWMsR0FBSSxXQUFtQixFQUFFLEtBQUssRUFBRSxVQUFVLEtBQUssTUFBTSxDQUFDO0lBQzFFLE9BQU87UUFDTCxDQUFDLFVBQVUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLG9CQUFvQixHQUFHLEtBQUssRUFBRSxFQUFFLENBQUMsd0JBQXdCLENBQUM7WUFDbkcsY0FBYyxDQUFBLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFdBQVc7WUFDaEM7Z0JBQ0UsT0FBTyxFQUFFLHVCQUF1QjtnQkFDaEMsS0FBSyxFQUFFLElBQUk7Z0JBQ1gsVUFBVSxFQUFFLENBQ1YsZ0JBQWdCLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEVBQzNDLEtBQUssR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQzNCLEVBQUUsQ0FBQyxHQUFHLEVBQUU7b0JBQ1IsSUFBSSxvQkFBb0IsRUFBRTt3QkFDeEIsb0RBQW9EO3FCQUNyRDtvQkFDRCxnQkFBZ0IsQ0FBQyxtQkFBbUIsQ0FDbEMsaUJBQWlCLENBQUMsS0FBSyxDQUFDLENBQ3pCLENBQUM7Z0JBQ0osQ0FBQzthQUNGO1NBQ0YsQ0FBQztRQUNGLENBQUMsU0FBUyxVQUFVLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQzFELE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFDbkIsRUFBRSxRQUFRLEVBQUUsbUJBQW1CLEVBQUUsRUFBRSxDQUNwQztLQUNvQyxDQUFDO0FBQzFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFTlZJUk9OTUVOVF9JTklUSUFMSVpFUiwgaW5qZWN0LCBtYWtlRW52aXJvbm1lbnRQcm92aWRlcnMgfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xyXG5pbXBvcnQgeyBBY3Rpb25DcmVhdG9yLCBBY3Rpb25UeXBlIH0gZnJvbSBcIkBuZ3J4L3N0b3JlL3NyYy9tb2RlbHNcIjtcclxuaW1wb3J0IHsgQ3JlYXRlUmVkdXhTdGF0ZSwgRXh0cmFjdEFjdGlvblR5cGVzLCBNYXBwZXJUeXBlcywgU3RvcmUgfSBmcm9tIFwiLi9tb2RlbFwiO1xyXG5pbXBvcnQgeyBTaWduYWxSZWR1eFN0b3JlLCBpbmplY3RSZWR1eERpc3BhdGNoIH0gZnJvbSBcIi4vc2lnbmFsLXJlZHV4LXN0b3JlXCI7XHJcbmltcG9ydCB7IGNhcGl0YWxpemUsIGlzQWN0aW9uQ3JlYXRvciB9IGZyb20gXCIuL3V0aWxcIjtcclxuXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gbWFwQWN0aW9uPFxyXG4gIENyZWF0b3JzIGV4dGVuZHMgcmVhZG9ubHkgQWN0aW9uQ3JlYXRvcltdXHJcbj4oXHJcbiAgLi4uYXJnczogW1xyXG4gICAgLi4uY3JlYXRvcnM6IENyZWF0b3JzLFxyXG4gICAgc3RvcmVNZXRob2Q6IChhY3Rpb246IEFjdGlvblR5cGU8Q3JlYXRvcnNbbnVtYmVyXT4pID0+IHVua25vd25cclxuICBdXHJcbik6IE1hcHBlclR5cGVzPENyZWF0b3JzPjtcclxuZXhwb3J0IGZ1bmN0aW9uIG1hcEFjdGlvbjxcclxuICBDcmVhdG9ycyBleHRlbmRzIHJlYWRvbmx5IEFjdGlvbkNyZWF0b3JbXSxcclxuICBUXHJcbj4oXHJcbiAgLi4uYXJnczogW1xyXG4gICAgLi4uY3JlYXRvcnM6IENyZWF0b3JzLFxyXG4gICAgc3RvcmVNZXRob2Q6IChhY3Rpb246IEFjdGlvblR5cGU8Q3JlYXRvcnNbbnVtYmVyXT4sIHJlc3VsdE1ldGhvZDogKGlucHV0OiBUKSA9PiB1bmtub3duKSA9PiB1bmtub3duLFxyXG4gICAgcmVzdWx0TWV0aG9kOiAoaW5wdXQ6IFQpID0+IHVua25vd25cclxuICBdXHJcbik6IE1hcHBlclR5cGVzPENyZWF0b3JzPjtcclxuZXhwb3J0IGZ1bmN0aW9uIG1hcEFjdGlvbjxcclxuICBDcmVhdG9ycyBleHRlbmRzIHJlYWRvbmx5IEFjdGlvbkNyZWF0b3JbXVxyXG4+KFxyXG4gIC4uLmFyZ3M6IFtcclxuICAgIC4uLmNyZWF0b3JzOiBDcmVhdG9ycyxcclxuICAgIHN0b3JlTWV0aG9kOiAoYWN0aW9uOiBBY3Rpb25UeXBlPENyZWF0b3JzW251bWJlcl0+KSA9PiB1bmtub3duLFxyXG4gICAgcmVzdWx0TWV0aG9kPzogKGlucHV0OiB1bmtub3duKSA9PiB1bmtub3duXHJcbiAgXVxyXG4pOiBNYXBwZXJUeXBlczxDcmVhdG9ycz4ge1xyXG4gIGxldCByZXN1bHRNZXRob2QgPSBhcmdzLnBvcCgpIGFzIHVua25vd24gYXMgKChpbnB1dDogdW5rbm93bikgPT4gdW5rbm93biApIHwgdW5kZWZpbmVkO1xyXG4gIGxldCBzdG9yZU1ldGhvZCA9IGFyZ3MucG9wKCkgYXMgdW5rbm93biBhcyAoYWN0aW9uOiBBY3Rpb25UeXBlPENyZWF0b3JzW251bWJlcl0+KSA9PiB1bmtub3duO1xyXG5cclxuICBpZiAoaXNBY3Rpb25DcmVhdG9yKHN0b3JlTWV0aG9kKSkge1xyXG4gICAgYXJncy5wdXNoKHN0b3JlTWV0aG9kKTtcclxuICAgIHN0b3JlTWV0aG9kID0gcmVzdWx0TWV0aG9kIHx8IHN0b3JlTWV0aG9kO1xyXG4gICAgcmVzdWx0TWV0aG9kID0gdW5kZWZpbmVkO1xyXG4gIH1cclxuXHJcbiAgY29uc3QgdHlwZXMgPSAoYXJncyBhcyB1bmtub3duIGFzIENyZWF0b3JzKS5tYXAoXHJcbiAgICAoY3JlYXRvcikgPT4gY3JlYXRvci50eXBlXHJcbiAgKSBhcyB1bmtub3duIGFzIEV4dHJhY3RBY3Rpb25UeXBlczxDcmVhdG9ycz47XHJcblxyXG4gIHJldHVybiB7XHJcbiAgICB0eXBlcyxcclxuICAgIHN0b3JlTWV0aG9kLFxyXG4gICAgcmVzdWx0TWV0aG9kXHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHdpdGhBY3Rpb25NYXBwZXJzKFxyXG4gIC4uLm1hcHBlcnM6IE1hcHBlclR5cGVzPEFjdGlvbkNyZWF0b3I8YW55LCBhbnk+W10+W11cclxuKTogTWFwcGVyVHlwZXM8QWN0aW9uQ3JlYXRvcjxhbnksIGFueT5bXT5bXSB7XHJcbiAgcmV0dXJuIG1hcHBlcnM7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWR1eFN0YXRlPFxyXG4gIFN0b3JlTmFtZSBleHRlbmRzIHN0cmluZyxcclxuICBTVE9SRSBleHRlbmRzIFN0b3JlXHJcbj4oXHJcbiAgc3RvcmVOYW1lOiBTdG9yZU5hbWUsXHJcbiAgc2lnbmFsU3RvcmU6IFNUT1JFLFxyXG4gIHdpdGhBY3Rpb25NYXBwZXJzOiAoc3RvcmU6IEluc3RhbmNlVHlwZTxTVE9SRT4pID0+IE1hcHBlclR5cGVzPEFjdGlvbkNyZWF0b3I8YW55LCBhbnk+W10+W10sXHJcbik6IENyZWF0ZVJlZHV4U3RhdGU8U3RvcmVOYW1lLCBTVE9SRT4ge1xyXG4gIGNvbnN0IGlzUm9vdFByb3ZpZGVyID0gKHNpZ25hbFN0b3JlIGFzIGFueSk/Lsm1cHJvdj8ucHJvdmlkZWRJbiA9PT0gJ3Jvb3QnO1xyXG4gIHJldHVybiB7XHJcbiAgICBbYHByb3ZpZGUke2NhcGl0YWxpemUoc3RvcmVOYW1lKX1TdG9yZWBdOiAoY29ubmVjdFJlZHV4RGV2dG9vbHMgPSBmYWxzZSkgPT4gbWFrZUVudmlyb25tZW50UHJvdmlkZXJzKFtcclxuICAgICAgaXNSb290UHJvdmlkZXI/IFtdIDogc2lnbmFsU3RvcmUsXHJcbiAgICAgIHtcclxuICAgICAgICBwcm92aWRlOiBFTlZJUk9OTUVOVF9JTklUSUFMSVpFUixcclxuICAgICAgICBtdWx0aTogdHJ1ZSxcclxuICAgICAgICB1c2VGYWN0b3J5OiAoXHJcbiAgICAgICAgICBzaWduYWxSZWR1eFN0b3JlID0gaW5qZWN0KFNpZ25hbFJlZHV4U3RvcmUpLFxyXG4gICAgICAgICAgc3RvcmUgPSBpbmplY3Qoc2lnbmFsU3RvcmUpXHJcbiAgICAgICAgKSA9PiAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoY29ubmVjdFJlZHV4RGV2dG9vbHMpIHtcclxuICAgICAgICAgICAgLy8gYWRkU3RvcmVUb1JlZHV4RGV2dG9vbHMoc3RvcmUsIHN0b3JlTmFtZSwgZmFsc2UpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgc2lnbmFsUmVkdXhTdG9yZS5jb25uZWN0RmVhdHVyZVN0b3JlKFxyXG4gICAgICAgICAgICB3aXRoQWN0aW9uTWFwcGVycyhzdG9yZSlcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICBdKSxcclxuICAgIFtgaW5qZWN0JHtjYXBpdGFsaXplKHN0b3JlTmFtZSl9U3RvcmVgXTogKCkgPT4gT2JqZWN0LmFzc2lnbihcclxuICAgICAgaW5qZWN0KHNpZ25hbFN0b3JlKSxcclxuICAgICAgeyBkaXNwYXRjaDogaW5qZWN0UmVkdXhEaXNwYXRjaCgpIH1cclxuICAgIClcclxuICB9IGFzIENyZWF0ZVJlZHV4U3RhdGU8U3RvcmVOYW1lLCBTVE9SRT47XHJcbn1cclxuIl19
|