platform
platform copied to clipboard
Expose more models to the public API: SignalStoreFeatureResult and InputSignalStore<Input>
Which @ngrx/* package(s) are relevant/related to the feature request?
signals
Information
In order to add more complexe features, it would be great to expose more models to the public API.
Here is an example of what I try to achieve
export const ExampleStore = signalStore(
withState<ExampleState>({ field: 12 }),
withMethods(
(store) => ({
log(text: string): void {
console.log(text)
},
}),
),
withTabVisibility({
onTabVisible: (store) => store.log('tab is visible: ' + store.field())
}),
});
It is possible but I used many unexposed models:
import { SignalStoreFeature, StateSignal } from '@ngrx/signals';
import type {
EmptyFeatureResult,
SignalStoreFeatureResult,
SignalStoreSlices,
} from '@ngrx/signals/src/signal-store-models';
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type TabVisibilityTrigger<Input extends SignalStoreFeatureResult> = (
store: Prettify<
SignalStoreSlices<Input['state']> &
Input['signals'] &
Input['methods'] &
StateSignal<Prettify<Input['state']>>
>,
) => void;
export function withTabVisibility<
Input extends SignalStoreFeatureResult,
>(options: {
onTabVisible?: TabVisibilityTrigger<Input>;
onTabHidden?: TabVisibilityTrigger<Input>;
}): SignalStoreFeature<Input, EmptyFeatureResult> {
return (store) => {
const inputStore = {
...store, // Cannot use [STATE_SIGNAL] because it's not exposed
...store.slices,
...store.signals,
...store.methods,
};
return {
...store,
hooks: {
onInit: () => {
store.hooks.onInit?.();
console.log('onInit');
document.addEventListener('visibilitychange', () =>
document.hidden
? options.onTabHidden?.(inputStore)
: options.onTabVisible?.(inputStore),
);
},
},
};
};
}
What would be marvelous would be to:
- Expose
EmptyFeatureResult
andSignalStoreFeatureResult
- Create a type
SignalStoreFeatureInputStore<Input>
(could be used bywithMethods
andwithHooks
) - Create a function
createFeatureInputStore
(could be used bywithMethods
andwithHooks
as well)
I could have written:
import { SignalStoreFeature, StateSignal, createFeatureInputStore, SignalStoreFeatureResult, EmptyFeatureResult, SignalStoreFeatureInputStore } from '@ngrx/signals';
type TabVisibilityTrigger<Input extends SignalStoreFeatureResult> = (
store: SignalStoreFeatureInputStore<Input>,
) => void;
export function withTabVisibility<
Input extends SignalStoreFeatureResult,
>(options: {
onTabVisible?: TabVisibilityTrigger<Input>;
onTabHidden?: TabVisibilityTrigger<Input>;
}): SignalStoreFeature<Input, EmptyFeatureResult> {
return (store) => {
const inputStore = createFeatureInputStore(store);
return {
...store,
hooks: {
onInit: () => {
store.hooks.onInit?.();
console.log('onInit');
document.addEventListener('visibilitychange', () =>
document.hidden
? options.onTabHidden?.(inputStore)
: options.onTabVisible?.(inputStore),
);
},
},
};
};
}
Thanks for this awesome lib ❤️
Describe any alternatives/workarounds you're currently using
It is possible to add hooks with:
const withTabVisiblity = signalStoreFeature(
{
methods: type<Partial<{ onTabVisible(): void; onTabHidden(): void }>>(),
}
// ...
)
Then:
export const ExampleStore = signalStore(
withState<ExampleState>({}),
withMethods(
(store) => ({
onTabVisible(): void {
console.log('tab is visible')
},
}),
),
withTabVisibility(),
The onTabVisible
method seems completely decoupled from the withTabVisibility
feature.
I would be willing to submit a PR to fix this issue
- [X] Yes
- [ ] No