ngrx / platform

Reactive State for Angular
https://ngrx.io
Other
7.96k stars 1.95k forks source link

Allow generics in ngrx signals withEntites #4339

Closed MeMeMax closed 2 months ago

MeMeMax commented 2 months ago

Which @ngrx/* package(s) are relevant/related to the feature request?

signals

Information

I would like to store objects with different characteristics in the entity management.

Imagine you have a class "Controls" and several subclasses like "Button", "InputField", "OutputField", .... They have common fields like "id", "isVisible" and also specific fields like "placeholder" for the "InputField".

With the current Definitions I can't achieve this in a typesafe way because withEntities doesn't allow generics. If I could pass a generic there and define which Type it is at component level this would work.

I think of something like this: export const ControlsStore = signalStore(withEntities<T>());

In a potential Button Component I would use it like this: controlsStore = inject(ControlsStore<ButtonControl>);

Describe any alternatives/workarounds you're currently using

Currently I am typing it like this:

export type Control<T> = T & {
  id: number;
  visible: boolean;
};

export interface ButtonControl {
  displayText: string;
}

export interface InputControl {
  placeholder: string;
}

export const ControlsStore = signalStore(withEntities<Control<any>>());

And using it in the ButtonComponent like this:

export class ButtonComponent {
  controlsStore = inject(ControlsStore);

  ngOnInit() {
    patchState(this.controlsStore, addEntity<Control<ButtonControl>>({ id: 1, visible: true, displayText: 'test' }));
  }
}

I would be willing to submit a PR to fix this issue

markostanimirovic commented 2 months ago

I think of something like this: export const ControlsStore = signalStore(withEntities<T>());

This is not the way how generics in TypeScript work - you cannot define a generic at the variable level. However, you can create a SignalStore factory function in the following way:

import { signalStore } from '@ngrx/signals';
import { withEntities } from '@ngrx/signals/entities';

export type Control<T> = T & {
  id: number;
  visible: boolean;
};

export interface ButtonControl {
  displayText: string;
}

export interface InputControl {
  placeholder: string;
}

// 👇
function createControlStore<T>() {
  return signalStore(withEntities<Control<T>>());
}

const ButtonStore = createControlStore<ButtonControl>();
const InputStore = createControlStore<InputControl>();

We use GitHub issues for bug reports and feature requests. For questions, you can use GitHub Discussions, Stackoverflow, or the official NgRx Discord server: https://discord.gg/ngrx

MeMeMax commented 2 months ago

Thanks for your response. Unfortunately a factory function won´t help since I (and usually everyone using a signalStore) need a singleton across the application.

I know this doesn't work export const ControlsStore = signalStore(withEntities<T>()); I just wanted to emphazise that I need something to make it generic.