@angular-architects/ngrx-toolkit 20.0.2 → 20.0.3
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/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +119 -0
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +1 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +1789 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
- package/index.d.ts +945 -0
- package/package.json +21 -4
- package/redux-connector/index.d.ts +59 -0
- package/eslint.config.cjs +0 -43
- package/jest.config.ts +0 -22
- package/ng-package.json +0 -7
- package/project.json +0 -37
- package/redux-connector/docs/README.md +0 -131
- package/redux-connector/index.ts +0 -6
- package/redux-connector/ng-package.json +0 -5
- package/redux-connector/src/lib/create-redux.ts +0 -102
- package/redux-connector/src/lib/model.ts +0 -89
- package/redux-connector/src/lib/rxjs-interop/redux-method.ts +0 -66
- package/redux-connector/src/lib/signal-redux-store.ts +0 -59
- package/redux-connector/src/lib/util.ts +0 -22
- package/src/index.ts +0 -43
- package/src/lib/assertions/assertions.ts +0 -9
- package/src/lib/devtools/features/with-disabled-name-indicies.ts +0 -31
- package/src/lib/devtools/features/with-glitch-tracking.ts +0 -35
- package/src/lib/devtools/features/with-mapper.ts +0 -34
- package/src/lib/devtools/internal/current-action-names.ts +0 -1
- package/src/lib/devtools/internal/default-tracker.ts +0 -60
- package/src/lib/devtools/internal/devtools-feature.ts +0 -37
- package/src/lib/devtools/internal/devtools-syncer.service.ts +0 -202
- package/src/lib/devtools/internal/glitch-tracker.service.ts +0 -61
- package/src/lib/devtools/internal/models.ts +0 -29
- package/src/lib/devtools/provide-devtools-config.ts +0 -32
- package/src/lib/devtools/rename-devtools-name.ts +0 -21
- package/src/lib/devtools/tests/action-name.spec.ts +0 -48
- package/src/lib/devtools/tests/basic.spec.ts +0 -111
- package/src/lib/devtools/tests/connecting.spec.ts +0 -37
- package/src/lib/devtools/tests/helpers.spec.ts +0 -43
- package/src/lib/devtools/tests/naming.spec.ts +0 -216
- package/src/lib/devtools/tests/provide-devtools-config.spec.ts +0 -25
- package/src/lib/devtools/tests/types.spec.ts +0 -19
- package/src/lib/devtools/tests/update-state.spec.ts +0 -29
- package/src/lib/devtools/tests/with-devtools.spec.ts +0 -5
- package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +0 -272
- package/src/lib/devtools/tests/with-mapper.spec.ts +0 -69
- package/src/lib/devtools/update-state.ts +0 -38
- package/src/lib/devtools/with-dev-tools-stub.ts +0 -6
- package/src/lib/devtools/with-devtools.ts +0 -81
- package/src/lib/immutable-state/deep-freeze.ts +0 -43
- package/src/lib/immutable-state/is-dev-mode.ts +0 -6
- package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +0 -278
- package/src/lib/immutable-state/with-immutable-state.ts +0 -150
- package/src/lib/shared/prettify.ts +0 -3
- package/src/lib/shared/signal-store-models.ts +0 -30
- package/src/lib/shared/throw-if-null.ts +0 -7
- package/src/lib/storage-sync/features/with-indexed-db.ts +0 -81
- package/src/lib/storage-sync/features/with-local-storage.ts +0 -58
- package/src/lib/storage-sync/internal/indexeddb.service.ts +0 -124
- package/src/lib/storage-sync/internal/local-storage.service.ts +0 -19
- package/src/lib/storage-sync/internal/models.ts +0 -62
- package/src/lib/storage-sync/internal/session-storage.service.ts +0 -18
- package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +0 -99
- package/src/lib/storage-sync/tests/with-storage-async.spec.ts +0 -305
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -273
- package/src/lib/storage-sync/with-storage-sync.ts +0 -236
- package/src/lib/with-call-state.spec.ts +0 -42
- package/src/lib/with-call-state.ts +0 -195
- package/src/lib/with-conditional.spec.ts +0 -125
- package/src/lib/with-conditional.ts +0 -74
- package/src/lib/with-data-service.spec.ts +0 -564
- package/src/lib/with-data-service.ts +0 -433
- package/src/lib/with-feature-factory.spec.ts +0 -69
- package/src/lib/with-feature-factory.ts +0 -56
- package/src/lib/with-pagination.spec.ts +0 -135
- package/src/lib/with-pagination.ts +0 -373
- package/src/lib/with-redux.spec.ts +0 -258
- package/src/lib/with-redux.ts +0 -387
- package/src/lib/with-reset.spec.ts +0 -112
- package/src/lib/with-reset.ts +0 -62
- package/src/lib/with-undo-redo.spec.ts +0 -274
- package/src/lib/with-undo-redo.ts +0 -200
- package/src/test-setup.ts +0 -6
- package/tsconfig.json +0 -29
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -17
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
import { ProviderToken, Signal, computed, inject } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
EmptyFeatureResult,
|
|
4
|
-
SignalStoreFeature,
|
|
5
|
-
WritableStateSource,
|
|
6
|
-
patchState,
|
|
7
|
-
signalStoreFeature,
|
|
8
|
-
withComputed,
|
|
9
|
-
withMethods,
|
|
10
|
-
withState,
|
|
11
|
-
} from '@ngrx/signals';
|
|
12
|
-
import {
|
|
13
|
-
EntityId,
|
|
14
|
-
NamedEntityState,
|
|
15
|
-
addEntity,
|
|
16
|
-
removeEntity,
|
|
17
|
-
setAllEntities,
|
|
18
|
-
updateEntity,
|
|
19
|
-
} from '@ngrx/signals/entities';
|
|
20
|
-
import { EntityState } from './shared/signal-store-models';
|
|
21
|
-
import {
|
|
22
|
-
CallState,
|
|
23
|
-
NamedCallStateSlice,
|
|
24
|
-
getCallStateKeys,
|
|
25
|
-
setError,
|
|
26
|
-
setLoaded,
|
|
27
|
-
setLoading,
|
|
28
|
-
} from './with-call-state';
|
|
29
|
-
|
|
30
|
-
export type Filter = Record<string, unknown>;
|
|
31
|
-
export type Entity = { id: EntityId };
|
|
32
|
-
|
|
33
|
-
export interface DataService<E extends Entity, F extends Filter> {
|
|
34
|
-
load(filter: F): Promise<E[]>;
|
|
35
|
-
|
|
36
|
-
loadById(id: EntityId): Promise<E>;
|
|
37
|
-
|
|
38
|
-
create(entity: E): Promise<E>;
|
|
39
|
-
|
|
40
|
-
update(entity: E): Promise<E>;
|
|
41
|
-
|
|
42
|
-
updateAll(entity: E[]): Promise<E[]>;
|
|
43
|
-
|
|
44
|
-
delete(entity: E): Promise<void>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function capitalize(str: string): string {
|
|
48
|
-
return str ? str[0].toUpperCase() + str.substring(1) : str;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function getDataServiceKeys(options: { collection?: string }) {
|
|
52
|
-
const filterKey = options.collection
|
|
53
|
-
? `${options.collection}Filter`
|
|
54
|
-
: 'filter';
|
|
55
|
-
const selectedIdsKey = options.collection
|
|
56
|
-
? `selected${capitalize(options.collection)}Ids`
|
|
57
|
-
: 'selectedIds';
|
|
58
|
-
const selectedEntitiesKey = options.collection
|
|
59
|
-
? `selected${capitalize(options.collection)}Entities`
|
|
60
|
-
: 'selectedEntities';
|
|
61
|
-
|
|
62
|
-
const updateFilterKey = options.collection
|
|
63
|
-
? `update${capitalize(options.collection)}Filter`
|
|
64
|
-
: 'updateFilter';
|
|
65
|
-
const updateSelectedKey = options.collection
|
|
66
|
-
? `updateSelected${capitalize(options.collection)}Entities`
|
|
67
|
-
: 'updateSelected';
|
|
68
|
-
const loadKey = options.collection
|
|
69
|
-
? `load${capitalize(options.collection)}Entities`
|
|
70
|
-
: 'load';
|
|
71
|
-
|
|
72
|
-
const currentKey = options.collection
|
|
73
|
-
? `current${capitalize(options.collection)}`
|
|
74
|
-
: 'current';
|
|
75
|
-
const loadByIdKey = options.collection
|
|
76
|
-
? `load${capitalize(options.collection)}ById`
|
|
77
|
-
: 'loadById';
|
|
78
|
-
const setCurrentKey = options.collection
|
|
79
|
-
? `setCurrent${capitalize(options.collection)}`
|
|
80
|
-
: 'setCurrent';
|
|
81
|
-
const createKey = options.collection
|
|
82
|
-
? `create${capitalize(options.collection)}`
|
|
83
|
-
: 'create';
|
|
84
|
-
const updateKey = options.collection
|
|
85
|
-
? `update${capitalize(options.collection)}`
|
|
86
|
-
: 'update';
|
|
87
|
-
const updateAllKey = options.collection
|
|
88
|
-
? `updateAll${capitalize(options.collection)}`
|
|
89
|
-
: 'updateAll';
|
|
90
|
-
const deleteKey = options.collection
|
|
91
|
-
? `delete${capitalize(options.collection)}`
|
|
92
|
-
: 'delete';
|
|
93
|
-
|
|
94
|
-
// TODO: Take these from @ngrx/signals/entities, when they are exported
|
|
95
|
-
const entitiesKey = options.collection
|
|
96
|
-
? `${options.collection}Entities`
|
|
97
|
-
: 'entities';
|
|
98
|
-
const entityMapKey = options.collection
|
|
99
|
-
? `${options.collection}EntityMap`
|
|
100
|
-
: 'entityMap';
|
|
101
|
-
const idsKey = options.collection ? `${options.collection}Ids` : 'ids';
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
filterKey,
|
|
105
|
-
selectedIdsKey,
|
|
106
|
-
selectedEntitiesKey,
|
|
107
|
-
updateFilterKey,
|
|
108
|
-
updateSelectedKey,
|
|
109
|
-
loadKey,
|
|
110
|
-
entitiesKey,
|
|
111
|
-
entityMapKey,
|
|
112
|
-
idsKey,
|
|
113
|
-
|
|
114
|
-
currentKey,
|
|
115
|
-
loadByIdKey,
|
|
116
|
-
setCurrentKey,
|
|
117
|
-
createKey,
|
|
118
|
-
updateKey,
|
|
119
|
-
updateAllKey,
|
|
120
|
-
deleteKey,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export type NamedDataServiceState<
|
|
125
|
-
E extends Entity,
|
|
126
|
-
F extends Filter,
|
|
127
|
-
Collection extends string,
|
|
128
|
-
> = {
|
|
129
|
-
[K in Collection as `${K}Filter`]: F;
|
|
130
|
-
} & {
|
|
131
|
-
[K in Collection as `selected${Capitalize<K>}Ids`]: Record<EntityId, boolean>;
|
|
132
|
-
} & {
|
|
133
|
-
[K in Collection as `current${Capitalize<K>}`]: E;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
export type DataServiceState<E extends Entity, F extends Filter> = {
|
|
137
|
-
filter: F;
|
|
138
|
-
selectedIds: Record<EntityId, boolean>;
|
|
139
|
-
current: E;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export type DataServiceComputed<E extends Entity> = {
|
|
143
|
-
selectedEntities: Signal<E[]>;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export type NamedDataServiceComputed<
|
|
147
|
-
E extends Entity,
|
|
148
|
-
Collection extends string,
|
|
149
|
-
> = {
|
|
150
|
-
[K in Collection as `selected${Capitalize<K>}Entities`]: Signal<E[]>;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
export type NamedDataServiceMethods<
|
|
154
|
-
E extends Entity,
|
|
155
|
-
F extends Filter,
|
|
156
|
-
Collection extends string,
|
|
157
|
-
> = {
|
|
158
|
-
[K in Collection as `update${Capitalize<K>}Filter`]: (filter: F) => void;
|
|
159
|
-
} & {
|
|
160
|
-
[K in Collection as `updateSelected${Capitalize<K>}Entities`]: (
|
|
161
|
-
id: EntityId,
|
|
162
|
-
selected: boolean,
|
|
163
|
-
) => void;
|
|
164
|
-
} & {
|
|
165
|
-
[K in Collection as `load${Capitalize<K>}Entities`]: () => Promise<void>;
|
|
166
|
-
} & {
|
|
167
|
-
[K in Collection as `setCurrent${Capitalize<K>}`]: (entity: E) => void;
|
|
168
|
-
} & {
|
|
169
|
-
[K in Collection as `load${Capitalize<K>}ById`]: (
|
|
170
|
-
id: EntityId,
|
|
171
|
-
) => Promise<void>;
|
|
172
|
-
} & {
|
|
173
|
-
[K in Collection as `create${Capitalize<K>}`]: (entity: E) => Promise<void>;
|
|
174
|
-
} & {
|
|
175
|
-
[K in Collection as `update${Capitalize<K>}`]: (entity: E) => Promise<void>;
|
|
176
|
-
} & {
|
|
177
|
-
[K in Collection as `updateAll${Capitalize<K>}`]: (
|
|
178
|
-
entity: E[],
|
|
179
|
-
) => Promise<void>;
|
|
180
|
-
} & {
|
|
181
|
-
[K in Collection as `delete${Capitalize<K>}`]: (entity: E) => Promise<void>;
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export type DataServiceMethods<E extends Entity, F extends Filter> = {
|
|
185
|
-
updateFilter: (filter: F) => void;
|
|
186
|
-
updateSelected: (id: EntityId, selected: boolean) => void;
|
|
187
|
-
load: () => Promise<void>;
|
|
188
|
-
|
|
189
|
-
setCurrent(entity: E): void;
|
|
190
|
-
loadById(id: EntityId): Promise<void>;
|
|
191
|
-
create(entity: E): Promise<void>;
|
|
192
|
-
update(entity: E): Promise<void>;
|
|
193
|
-
updateAll(entities: E[]): Promise<void>;
|
|
194
|
-
delete(entity: E): Promise<void>;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
export function withDataService<
|
|
198
|
-
E extends Entity,
|
|
199
|
-
F extends Filter,
|
|
200
|
-
Collection extends string,
|
|
201
|
-
>(options: {
|
|
202
|
-
dataServiceType: ProviderToken<DataService<E, F>>;
|
|
203
|
-
filter: F;
|
|
204
|
-
collection: Collection;
|
|
205
|
-
}): SignalStoreFeature<
|
|
206
|
-
EmptyFeatureResult & {
|
|
207
|
-
state: NamedCallStateSlice<Collection> & NamedEntityState<E, Collection>;
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
state: NamedDataServiceState<E, F, Collection>;
|
|
211
|
-
props: NamedDataServiceComputed<E, Collection>;
|
|
212
|
-
methods: NamedDataServiceMethods<E, F, Collection>;
|
|
213
|
-
}
|
|
214
|
-
>;
|
|
215
|
-
export function withDataService<E extends Entity, F extends Filter>(options: {
|
|
216
|
-
dataServiceType: ProviderToken<DataService<E, F>>;
|
|
217
|
-
filter: F;
|
|
218
|
-
}): SignalStoreFeature<
|
|
219
|
-
EmptyFeatureResult & { state: { callState: CallState } & EntityState<E> },
|
|
220
|
-
{
|
|
221
|
-
state: DataServiceState<E, F>;
|
|
222
|
-
props: DataServiceComputed<E>;
|
|
223
|
-
methods: DataServiceMethods<E, F>;
|
|
224
|
-
}
|
|
225
|
-
>;
|
|
226
|
-
|
|
227
|
-
export function withDataService<
|
|
228
|
-
E extends Entity,
|
|
229
|
-
F extends Filter,
|
|
230
|
-
Collection extends string,
|
|
231
|
-
>(options: {
|
|
232
|
-
dataServiceType: ProviderToken<DataService<E, F>>;
|
|
233
|
-
filter: F;
|
|
234
|
-
collection?: Collection;
|
|
235
|
-
}): /* eslint-disable @typescript-eslint/no-explicit-any */
|
|
236
|
-
SignalStoreFeature<any, any> {
|
|
237
|
-
const { dataServiceType, filter, collection: prefix } = options;
|
|
238
|
-
const {
|
|
239
|
-
entitiesKey,
|
|
240
|
-
filterKey,
|
|
241
|
-
loadKey,
|
|
242
|
-
selectedEntitiesKey,
|
|
243
|
-
selectedIdsKey,
|
|
244
|
-
updateFilterKey,
|
|
245
|
-
updateSelectedKey,
|
|
246
|
-
|
|
247
|
-
currentKey,
|
|
248
|
-
createKey,
|
|
249
|
-
updateKey,
|
|
250
|
-
updateAllKey,
|
|
251
|
-
deleteKey,
|
|
252
|
-
loadByIdKey,
|
|
253
|
-
setCurrentKey,
|
|
254
|
-
} = getDataServiceKeys(options);
|
|
255
|
-
|
|
256
|
-
const { callStateKey } = getCallStateKeys({ collection: prefix });
|
|
257
|
-
|
|
258
|
-
return signalStoreFeature(
|
|
259
|
-
withState(() => ({
|
|
260
|
-
[filterKey]: filter,
|
|
261
|
-
[selectedIdsKey]: {} as Record<EntityId, boolean>,
|
|
262
|
-
[currentKey]: undefined as E | undefined,
|
|
263
|
-
})),
|
|
264
|
-
withComputed((store: Record<string, unknown>) => {
|
|
265
|
-
const entities = store[entitiesKey] as Signal<E[]>;
|
|
266
|
-
const selectedIds = store[selectedIdsKey] as Signal<
|
|
267
|
-
Record<EntityId, boolean>
|
|
268
|
-
>;
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
[selectedEntitiesKey]: computed(() =>
|
|
272
|
-
entities().filter((e) => selectedIds()[e.id]),
|
|
273
|
-
),
|
|
274
|
-
};
|
|
275
|
-
}),
|
|
276
|
-
withMethods(
|
|
277
|
-
(store: Record<string, unknown> & WritableStateSource<object>) => {
|
|
278
|
-
const dataService = inject(dataServiceType);
|
|
279
|
-
return {
|
|
280
|
-
[updateFilterKey]: (filter: F): void => {
|
|
281
|
-
patchState(store, { [filterKey]: filter });
|
|
282
|
-
},
|
|
283
|
-
[updateSelectedKey]: (id: EntityId, selected: boolean): void => {
|
|
284
|
-
patchState(store, (state: Record<string, unknown>) => ({
|
|
285
|
-
[selectedIdsKey]: {
|
|
286
|
-
...(state[selectedIdsKey] as Record<EntityId, boolean>),
|
|
287
|
-
[id]: selected,
|
|
288
|
-
},
|
|
289
|
-
}));
|
|
290
|
-
},
|
|
291
|
-
[loadKey]: async (): Promise<void> => {
|
|
292
|
-
const filter = store[filterKey] as Signal<F>;
|
|
293
|
-
(() =>
|
|
294
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
const result = await dataService.load(filter());
|
|
298
|
-
patchState(
|
|
299
|
-
store,
|
|
300
|
-
prefix
|
|
301
|
-
? setAllEntities(result, { collection: prefix })
|
|
302
|
-
: setAllEntities(result),
|
|
303
|
-
);
|
|
304
|
-
(() =>
|
|
305
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
306
|
-
} catch (e) {
|
|
307
|
-
(() =>
|
|
308
|
-
store[callStateKey] &&
|
|
309
|
-
patchState(store, setError(e, prefix)))();
|
|
310
|
-
throw e;
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
[loadByIdKey]: async (id: EntityId): Promise<void> => {
|
|
314
|
-
(() =>
|
|
315
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
316
|
-
|
|
317
|
-
try {
|
|
318
|
-
const current = await dataService.loadById(id);
|
|
319
|
-
(() =>
|
|
320
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
321
|
-
patchState(store, { [currentKey]: current });
|
|
322
|
-
} catch (e) {
|
|
323
|
-
(() =>
|
|
324
|
-
store[callStateKey] &&
|
|
325
|
-
patchState(store, setError(e, prefix)))();
|
|
326
|
-
throw e;
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
[setCurrentKey]: (current: E): void => {
|
|
330
|
-
patchState(store, { [currentKey]: current });
|
|
331
|
-
},
|
|
332
|
-
[createKey]: async (entity: E): Promise<void> => {
|
|
333
|
-
patchState(store, { [currentKey]: entity });
|
|
334
|
-
(() =>
|
|
335
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
336
|
-
|
|
337
|
-
try {
|
|
338
|
-
const created = await dataService.create(entity);
|
|
339
|
-
patchState(store, { [currentKey]: created });
|
|
340
|
-
patchState(
|
|
341
|
-
store,
|
|
342
|
-
prefix
|
|
343
|
-
? addEntity(created, { collection: prefix })
|
|
344
|
-
: addEntity(created),
|
|
345
|
-
);
|
|
346
|
-
(() =>
|
|
347
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
348
|
-
} catch (e) {
|
|
349
|
-
(() =>
|
|
350
|
-
store[callStateKey] &&
|
|
351
|
-
patchState(store, setError(e, prefix)))();
|
|
352
|
-
throw e;
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
[updateKey]: async (entity: E): Promise<void> => {
|
|
356
|
-
patchState(store, { [currentKey]: entity });
|
|
357
|
-
(() =>
|
|
358
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
359
|
-
|
|
360
|
-
try {
|
|
361
|
-
const updated = await dataService.update(entity);
|
|
362
|
-
patchState(store, { [currentKey]: updated });
|
|
363
|
-
|
|
364
|
-
const updateArg = {
|
|
365
|
-
id: updated.id,
|
|
366
|
-
changes: updated,
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
const updater = (collection: string) =>
|
|
370
|
-
updateEntity(updateArg, { collection });
|
|
371
|
-
|
|
372
|
-
patchState(
|
|
373
|
-
store,
|
|
374
|
-
prefix ? updater(prefix) : updateEntity(updateArg),
|
|
375
|
-
);
|
|
376
|
-
(() =>
|
|
377
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
378
|
-
} catch (e) {
|
|
379
|
-
(() =>
|
|
380
|
-
store[callStateKey] &&
|
|
381
|
-
patchState(store, setError(e, prefix)))();
|
|
382
|
-
throw e;
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
[updateAllKey]: async (entities: E[]): Promise<void> => {
|
|
386
|
-
(() =>
|
|
387
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
388
|
-
|
|
389
|
-
try {
|
|
390
|
-
const result = await dataService.updateAll(entities);
|
|
391
|
-
patchState(
|
|
392
|
-
store,
|
|
393
|
-
prefix
|
|
394
|
-
? setAllEntities(result, { collection: prefix })
|
|
395
|
-
: setAllEntities(result),
|
|
396
|
-
);
|
|
397
|
-
(() =>
|
|
398
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
399
|
-
} catch (e) {
|
|
400
|
-
(() =>
|
|
401
|
-
store[callStateKey] &&
|
|
402
|
-
patchState(store, setError(e, prefix)))();
|
|
403
|
-
throw e;
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
[deleteKey]: async (entity: E): Promise<void> => {
|
|
407
|
-
patchState(store, { [currentKey]: entity });
|
|
408
|
-
(() =>
|
|
409
|
-
store[callStateKey] && patchState(store, setLoading(prefix)))();
|
|
410
|
-
|
|
411
|
-
try {
|
|
412
|
-
await dataService.delete(entity);
|
|
413
|
-
patchState(store, { [currentKey]: undefined });
|
|
414
|
-
patchState(
|
|
415
|
-
store,
|
|
416
|
-
prefix
|
|
417
|
-
? removeEntity(entity.id, { collection: prefix })
|
|
418
|
-
: removeEntity(entity.id),
|
|
419
|
-
);
|
|
420
|
-
(() =>
|
|
421
|
-
store[callStateKey] && patchState(store, setLoaded(prefix)))();
|
|
422
|
-
} catch (e) {
|
|
423
|
-
(() =>
|
|
424
|
-
store[callStateKey] &&
|
|
425
|
-
patchState(store, setError(e, prefix)))();
|
|
426
|
-
throw e;
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
},
|
|
431
|
-
),
|
|
432
|
-
);
|
|
433
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { computed, Signal } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import {
|
|
4
|
-
getState,
|
|
5
|
-
patchState,
|
|
6
|
-
signalStore,
|
|
7
|
-
signalStoreFeature,
|
|
8
|
-
withComputed,
|
|
9
|
-
withMethods,
|
|
10
|
-
withState,
|
|
11
|
-
} from '@ngrx/signals';
|
|
12
|
-
import { lastValueFrom, of } from 'rxjs';
|
|
13
|
-
import { withFeatureFactory } from './with-feature-factory';
|
|
14
|
-
|
|
15
|
-
type User = {
|
|
16
|
-
id: number;
|
|
17
|
-
name: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function withMyEntity<Entity>(loadMethod: (id: number) => Promise<Entity>) {
|
|
21
|
-
return signalStoreFeature(
|
|
22
|
-
withState({
|
|
23
|
-
currentId: 1 as number | undefined,
|
|
24
|
-
entity: undefined as undefined | Entity,
|
|
25
|
-
}),
|
|
26
|
-
withMethods((store) => ({
|
|
27
|
-
async load(id: number) {
|
|
28
|
-
const entity = await loadMethod(1);
|
|
29
|
-
patchState(store, { entity, currentId: id });
|
|
30
|
-
},
|
|
31
|
-
})),
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('withFeatureFactory', () => {
|
|
36
|
-
it('should allow a sum feature', () => {
|
|
37
|
-
function withSum(a: Signal<number>, b: Signal<number>) {
|
|
38
|
-
return signalStoreFeature(
|
|
39
|
-
withComputed(() => ({ sum: computed(() => a() + b()) })),
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
signalStore(
|
|
43
|
-
withState({ a: 1, b: 2 }),
|
|
44
|
-
withFeatureFactory((store) => withSum(store.a, store.b)),
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should allow to pass elements from a SignalStore to a feature', async () => {
|
|
49
|
-
const UserStore = signalStore(
|
|
50
|
-
{ providedIn: 'root' },
|
|
51
|
-
withMethods(() => ({
|
|
52
|
-
findById(id: number) {
|
|
53
|
-
return of({ id: 1, name: 'Konrad' });
|
|
54
|
-
},
|
|
55
|
-
})),
|
|
56
|
-
withFeatureFactory((store) => {
|
|
57
|
-
const loader = (id: number) => lastValueFrom(store.findById(id));
|
|
58
|
-
return withMyEntity<User>(loader);
|
|
59
|
-
}),
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const userStore = TestBed.inject(UserStore);
|
|
63
|
-
await userStore.load(1);
|
|
64
|
-
expect(getState(userStore)).toEqual({
|
|
65
|
-
currentId: 1,
|
|
66
|
-
entity: { id: 1, name: 'Konrad' },
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SignalStoreFeature,
|
|
3
|
-
SignalStoreFeatureResult,
|
|
4
|
-
StateSignals,
|
|
5
|
-
} from '@ngrx/signals';
|
|
6
|
-
|
|
7
|
-
type StoreForFactory<Input extends SignalStoreFeatureResult> = StateSignals<
|
|
8
|
-
Input['state']
|
|
9
|
-
> &
|
|
10
|
-
Input['props'] &
|
|
11
|
-
Input['methods'];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated Use `import { withFeature } from '@ngrx/signals'` instead, starting with `ngrx/signals` 19.1: https://ngrx.io/guide/signals/signal-store/custom-store-features#connecting-a-custom-feature-with-the-store
|
|
15
|
-
*
|
|
16
|
-
* Allows to pass properties, methods, or signals from a SignalStore
|
|
17
|
-
* to a feature.
|
|
18
|
-
*
|
|
19
|
-
* Typically, a `signalStoreFeature` can have input constraints on
|
|
20
|
-
*
|
|
21
|
-
* ```typescript
|
|
22
|
-
* function withSum(a: Signal<number>, b: Signal<number>) {
|
|
23
|
-
* return signalStoreFeature(
|
|
24
|
-
* withComputed(() => ({
|
|
25
|
-
* sum: computed(() => a() + b())
|
|
26
|
-
* }))
|
|
27
|
-
* );
|
|
28
|
-
* }
|
|
29
|
-
*
|
|
30
|
-
* signalStore(
|
|
31
|
-
* withState({ a: 1, b: 2 }),
|
|
32
|
-
* withFeatureFactory((store) => withSum(store.a, store.b))
|
|
33
|
-
* );
|
|
34
|
-
* ```
|
|
35
|
-
* @param factoryFn
|
|
36
|
-
*/
|
|
37
|
-
export function withFeatureFactory<
|
|
38
|
-
Input extends SignalStoreFeatureResult,
|
|
39
|
-
Output extends SignalStoreFeatureResult,
|
|
40
|
-
>(
|
|
41
|
-
factoryFn: (
|
|
42
|
-
store: StoreForFactory<Input>,
|
|
43
|
-
) => SignalStoreFeature<Input, Output>,
|
|
44
|
-
): SignalStoreFeature<Input, Output> {
|
|
45
|
-
return (store) => {
|
|
46
|
-
const storeForFactory = {
|
|
47
|
-
...store['stateSignals'],
|
|
48
|
-
...store['props'],
|
|
49
|
-
...store['methods'],
|
|
50
|
-
} as StoreForFactory<Input>;
|
|
51
|
-
|
|
52
|
-
const feature = factoryFn(storeForFactory);
|
|
53
|
-
|
|
54
|
-
return feature(store);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { patchState, signalStore, type } from '@ngrx/signals';
|
|
2
|
-
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
|
|
3
|
-
import {
|
|
4
|
-
createPageArray,
|
|
5
|
-
firstPage,
|
|
6
|
-
gotoPage,
|
|
7
|
-
nextPage,
|
|
8
|
-
previousPage,
|
|
9
|
-
setPageSize,
|
|
10
|
-
withPagination,
|
|
11
|
-
} from './with-pagination';
|
|
12
|
-
|
|
13
|
-
type Book = { id: number; title: string; author: string };
|
|
14
|
-
const generateBooks = (count = 10) => {
|
|
15
|
-
const books = [] as Book[];
|
|
16
|
-
for (let i = 1; i <= count; i++) {
|
|
17
|
-
books.push({ id: i, title: `Book ${i}`, author: `Author ${i}` });
|
|
18
|
-
}
|
|
19
|
-
return books;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
describe('withPagination', () => {
|
|
23
|
-
it('should use and update a pagination', () => {
|
|
24
|
-
const Store = signalStore(
|
|
25
|
-
{ protectedState: false },
|
|
26
|
-
withEntities({ entity: type<Book>() }),
|
|
27
|
-
withPagination(),
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const store = new Store();
|
|
31
|
-
|
|
32
|
-
patchState(store, setAllEntities(generateBooks(55)));
|
|
33
|
-
expect(store.currentPage()).toBe(0);
|
|
34
|
-
expect(store.pageCount()).toBe(6);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should use and update a pagination with collection', () => {
|
|
38
|
-
const Store = signalStore(
|
|
39
|
-
{ protectedState: false },
|
|
40
|
-
withEntities({ entity: type<Book>(), collection: 'books' }),
|
|
41
|
-
withPagination({ entity: type<Book>(), collection: 'books' }),
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const store = new Store();
|
|
45
|
-
|
|
46
|
-
patchState(
|
|
47
|
-
store,
|
|
48
|
-
setAllEntities(generateBooks(55), { collection: 'books' }),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
patchState(store, gotoPage(5, { collection: 'books' }));
|
|
52
|
-
expect(store.booksCurrentPage()).toBe(5);
|
|
53
|
-
expect(store.selectedPageBooksEntities().length).toBe(5);
|
|
54
|
-
expect(store.booksPageCount()).toBe(6);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should navigate through pages', () => {
|
|
58
|
-
const Store = signalStore(
|
|
59
|
-
{ protectedState: false },
|
|
60
|
-
withEntities({ entity: type<Book>() }),
|
|
61
|
-
withPagination(),
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const store = new Store();
|
|
65
|
-
|
|
66
|
-
patchState(store, setAllEntities(generateBooks(55)));
|
|
67
|
-
expect(store.currentPage()).toBe(0);
|
|
68
|
-
|
|
69
|
-
patchState(store, nextPage());
|
|
70
|
-
expect(store.currentPage()).toBe(1);
|
|
71
|
-
|
|
72
|
-
patchState(store, previousPage());
|
|
73
|
-
expect(store.currentPage()).toBe(0);
|
|
74
|
-
|
|
75
|
-
patchState(store, nextPage());
|
|
76
|
-
patchState(store, nextPage());
|
|
77
|
-
expect(store.currentPage()).toBe(2);
|
|
78
|
-
|
|
79
|
-
patchState(store, firstPage());
|
|
80
|
-
expect(store.currentPage()).toBe(0);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should set page size', () => {
|
|
84
|
-
const Store = signalStore(
|
|
85
|
-
{ protectedState: false },
|
|
86
|
-
withEntities({ entity: type<Book>() }),
|
|
87
|
-
withPagination(),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const store = new Store();
|
|
91
|
-
|
|
92
|
-
patchState(store, setAllEntities(generateBooks(50)), setPageSize(10));
|
|
93
|
-
expect(store.pageCount()).toBe(5);
|
|
94
|
-
|
|
95
|
-
patchState(store, setPageSize(5));
|
|
96
|
-
expect(store.pageCount()).toBe(10);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should react on entity changes', () => {
|
|
100
|
-
const Store = signalStore(
|
|
101
|
-
{ protectedState: false },
|
|
102
|
-
withEntities({ entity: type<Book>() }),
|
|
103
|
-
withPagination(),
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const store = new Store();
|
|
107
|
-
|
|
108
|
-
patchState(store, setAllEntities(generateBooks(100)));
|
|
109
|
-
|
|
110
|
-
expect(store.pageCount()).toBe(10);
|
|
111
|
-
|
|
112
|
-
patchState(store, setAllEntities(generateBooks(20)));
|
|
113
|
-
|
|
114
|
-
expect(store.pageCount()).toBe(2);
|
|
115
|
-
|
|
116
|
-
patchState(store, setPageSize(5));
|
|
117
|
-
|
|
118
|
-
expect(store.pageCount()).toBe(4);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('internal pageNavigationArray', () => {
|
|
122
|
-
it('should return an array of page numbers', () => {
|
|
123
|
-
const pages = createPageArray(8, 10, 500, 7);
|
|
124
|
-
expect(pages).toEqual([
|
|
125
|
-
{ label: 5, value: 5 },
|
|
126
|
-
{ label: '...', value: 6 },
|
|
127
|
-
{ label: 7, value: 7 },
|
|
128
|
-
{ label: 8, value: 8 },
|
|
129
|
-
{ label: 9, value: 9 },
|
|
130
|
-
{ label: '...', value: 10 },
|
|
131
|
-
{ label: 50, value: 50 },
|
|
132
|
-
]);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
});
|