ngrx / platform

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

(feat): Extend SignalsDictionary and MethodsDictionary #4497

Closed tmasnyk closed 2 months ago

tmasnyk commented 2 months ago

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

signals

Information

I've created my own SignalStoreFeature called withEntitiesManagement, which adds an entities collection, CRUD operations, and other functionality to manage it. I want to use several such features in my store:

export const ApplicationsModuleStore = signalStore(
...
 withEntitiesManagement('applications', ApplicationsService),
 withEntitiesManagement('applicationsGroups', ApplicationsGroupsService),
 withEntitiesManagement('applicationsLinks', ApplicationsLinksService),
...
)

However, this produces a large and flat list of named methods and 'selectors', which causes noticeable freezing during IDE type fetches as well. So, I decided to slice the store to be used like this:

    store = inject(ApplicationsModuleStore );

    const applicationEntities = this.store.applications.selectedItems();
    const applicationGroupsEntities = this.store.applicationsGroups.selectedItems();
    const applicationsLinksEntities = this.store.applicationsLinks.entities()
    ...
    this.store.applications.pager();
    this.store.applications.updateFilter(filter);
    this.store.applications.delete(applicationEntities[0]);
    this.store.applications.findAll();

    this.store.applicationsGroups.entities();
    this.store.applicationsGroups.updateFilter(filter);
    this.store.applicationsGroups.delete(applicationGroupsEntities[0]);
    this.store.applicationsGroups.findAll();
    ...

But there is a problem with the type limitations in the signal store:

export type SignalsDictionary = Record<string, Signal<unknown>>;
export type MethodsDictionary = Record<string, Function>;

These types don't work when I try to use them with slices, as they don't satisfy SignalStoreFeatureResult and InnerSignalStore:

export type EntitiesManagementComputed<E extends Entity, Collection extends string> = {
  [K in Collection]: {
    selectedItems: Signal<E[]>;
    pager: Signal<Pager>;
    entities: Signal<E[]>;
  }
};

export type EntitiesManagementMethods<E extends Entity, F extends Filter, Collection extends string> = {
  [K in Collection]: {
    updateFilter: (filter: F) => void;
    updateSelectedItems: (id: EntityId, selected: boolean) => void;
    findAll: (parentResourceId?: null | number) => Promise<void>;
    setCurrentItem: (entity: E | null) => void;
    findOneById: (id: EntityId) => Promise<void>;
    save: (entity: E) => Promise<void>;
    create: (data: Partial<E>, translatableProps: TranslatablePropertiesType<E>[]) => void;
    delete: (entity: E, parentResourceId?: number) => Promise<void>;
  };
};

export type EntitiesManagementFeatureResult<E extends Entity, F extends Filter, Collection extends string> = {
  state: {};
  computed: EntitiesManagementComputed<E, Collection>;
  methods: EntitiesManagementMethods<E, F, Collection>;
};

To use the store with slices, the types need to be something like this:

export type SignalsDictionary = Record<string,  Signal<unknown> | Record<string,  Signal<unknown>>>;
export type MethodsDictionary = Record<string, Function |  Record<string, Function>>;

Could the NgRx team extend these types?

Describe any alternatives/workarounds you're currently using

No response

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

rainerhahnekamp commented 2 months ago

Can I suggest an alternative?

Why don't you create multiple Stores, each responsible for one particular entity? This is also recommended for withEntities().

If you have the need to provide a single service for all three entities, you could have your three SignalStore and wrap a Service around them, which exposes each signalStore as property.

class ApplicationStore {
  readonly applications = inject(ApplicationStore);
  readonly links = inject(ApplicationLinksStore);
  readonly groups = inject(ApplicationGroupsStore);
}

I'm going to convert this issue to a discussion.