Skip to content

Commit 90a37ca

Browse files
feat: Enhanced withCallState to accept mutliple collection configuration (#148)
1 parent 87eea04 commit 90a37ca

File tree

2 files changed

+84
-15
lines changed

2 files changed

+84
-15
lines changed

libs/ngrx-toolkit/src/lib/with-call-state.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,19 @@ describe('withCallState', () => {
2424
expect(dataStore.entitiesCallState()).toBe('loaded');
2525
expect(dataStore.entitiesLoaded()).toBe(true);
2626
});
27+
28+
it('should use the callState for multiple collections with an array', () => {
29+
const DataStore = signalStore(
30+
{ protectedState: false },
31+
withCallState({ collections: ['entities', 'products'] })
32+
);
33+
const dataStore = new DataStore();
34+
35+
patchState(dataStore, setLoaded('entities'), setLoaded('products'));
36+
37+
expect(dataStore.entitiesCallState()).toBe('loaded');
38+
expect(dataStore.productsCallState()).toBe('loaded');
39+
expect(dataStore.entitiesLoaded()).toBe(true);
40+
expect(dataStore.productsLoaded()).toBe(true);
41+
});
2742
});

libs/ngrx-toolkit/src/lib/with-call-state.ts

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type CallStateSlice = {
1414
};
1515

1616
export type NamedCallStateSlice<Collection extends string> = {
17-
[K in keyof CallStateSlice as `${Collection}${Capitalize<K>}`]: CallStateSlice[K];
17+
[K in keyof CallStateSlice as Collection extends '' ? `${Collection}${K}` : `${Collection}${Capitalize<K>}`]: CallStateSlice[K];
1818
};
1919

2020
export type CallStateSignals = {
@@ -24,23 +24,44 @@ export type CallStateSignals = {
2424
};
2525

2626
export type NamedCallStateSignals<Prop extends string> = {
27-
[K in keyof CallStateSignals as `${Prop}${Capitalize<K>}`]: CallStateSignals[K];
27+
[K in keyof CallStateSignals as Prop extends '' ? `${Prop}${K}` : `${Prop}${Capitalize<K>}`]: CallStateSignals[K];
2828
};
2929

3030
export type SetCallState<Prop extends string | undefined> = Prop extends string
3131
? NamedCallStateSlice<Prop>
3232
: CallStateSlice;
3333

34-
export function getCallStateKeys(config?: { collection?: string }) {
35-
const prop = config?.collection;
34+
export function deriveCallStateKeys<Collection extends string>(collection?: Collection) {
3635
return {
37-
callStateKey: prop ? `${config.collection}CallState` : 'callState',
38-
loadingKey: prop ? `${config.collection}Loading` : 'loading',
39-
loadedKey: prop ? `${config.collection}Loaded` : 'loaded',
40-
errorKey: prop ? `${config.collection}Error` : 'error',
36+
callStateKey: collection ? `${collection}CallState` : 'callState',
37+
loadingKey: collection ? `${collection}Loading` : 'loading',
38+
loadedKey: collection ? `${collection}Loaded` : 'loaded',
39+
errorKey: collection ? `${collection}Error` : 'error',
4140
};
4241
}
4342

43+
export function getCallStateKeys(config?: { collection?: string}) {
44+
const prop = config?.collection;
45+
return deriveCallStateKeys(prop);
46+
}
47+
48+
export function getCollectionArray(config: { collection?: string } | { collections?: string[] }){
49+
return 'collections' in config
50+
? config.collections
51+
: 'collection' in config && config.collection
52+
? [config.collection]
53+
: undefined;
54+
}
55+
56+
export function withCallState<Collection extends string>(config: {
57+
collections: Collection[];
58+
}): SignalStoreFeature<
59+
EmptyFeatureResult,
60+
EmptyFeatureResult & {
61+
state: NamedCallStateSlice<Collection>;
62+
props: NamedCallStateSignals<Collection>;
63+
}
64+
>;
4465
export function withCallState<Collection extends string>(config: {
4566
collection: Collection;
4667
}): SignalStoreFeature<
@@ -56,18 +77,51 @@ export function withCallState(): SignalStoreFeature<
5677
state: CallStateSlice;
5778
props: CallStateSignals;
5879
}
59-
>;
60-
export function withCallState<Collection extends string>(config?: {
80+
>;export function withCallState<Collection extends string>(config?: {
6181
collection: Collection;
82+
} | {
83+
collections: Collection[];
6284
}): SignalStoreFeature {
63-
const { callStateKey, errorKey, loadedKey, loadingKey } =
64-
getCallStateKeys(config);
65-
6685
return signalStoreFeature(
67-
withState({ [callStateKey]: 'init' }),
86+
withState(() => {
87+
if (!config) {
88+
return { callState: 'init' };
89+
}
90+
const collections = getCollectionArray(config);
91+
if (collections) {
92+
return collections.reduce(
93+
(acc, cur) => ({
94+
...acc,
95+
...{ [cur ? `${cur}CallState` : 'callState']: 'init' },
96+
}),
97+
{}
98+
);
99+
}
100+
101+
return { callState: 'init' };
102+
}),
68103
withComputed((state: Record<string, Signal<unknown>>) => {
104+
if (config) {
105+
const collections = getCollectionArray(config);
106+
if (collections) {
107+
return collections.reduce<Record<string, Signal<unknown>>>((acc, cur: string) => {
108+
const { callStateKey, errorKey, loadedKey, loadingKey } =
109+
deriveCallStateKeys(cur);
110+
const callState = state[callStateKey] as Signal<CallState>;
111+
return {
112+
...acc,
113+
[loadingKey]: computed(() => callState() === 'loading'),
114+
[loadedKey]: computed(() => callState() === 'loaded'),
115+
[errorKey]: computed(() => {
116+
const v = callState();
117+
return typeof v === 'object' ? v.error : null;
118+
}),
119+
};
120+
}, {});
121+
}
122+
}
123+
const { callStateKey, errorKey, loadedKey, loadingKey } = deriveCallStateKeys();
69124
const callState = state[callStateKey] as Signal<CallState>;
70-
71125
return {
72126
[loadingKey]: computed(() => callState() === 'loading'),
73127
[loadedKey]: computed(() => callState() === 'loaded'),

0 commit comments

Comments
 (0)