Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose more models to the public API: SignalStoreFeatureResult and InputSignalStore<Input> #4272

Open
1 of 2 tasks
GuillaumeNury opened this issue Mar 6, 2024 · 0 comments

Comments

@GuillaumeNury
Copy link

GuillaumeNury commented Mar 6, 2024

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 and SignalStoreFeatureResult
  • Create a type SignalStoreFeatureInputStore<Input> (could be used by withMethods and withHooks)
  • Create a function createFeatureInputStore (could be used by withMethods and withHooks 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

  • Yes
  • No
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants