You can achieve something like this with a signalStoreFeature
. Like this article, "Extending the NgRx signal store with a custom feature"
A summary of the article:
The end product is adding this one line to your stores:
withCrudOperations<MyDto>(MyService),
MyService
implements an interface like this:
export interface CrudService<T> {
fetch(id: string): Observable<T>;
update(value: T): Observable<T>;
//... other ones
}
The signature of the signalStoreFeature
, and an example of a method in use:
export type BaseEntity = { id: string };
export type BaseState<Entity> = {
items: Entity[];
};
export function withCrudOperations<Entity extends BaseEntity>(
dataServiceType: Type<CrudService<Entity>>
) {
return signalStoreFeature(
{
state: type<BaseState<Entity>>(),
},
withMethods((store) => {
const service = inject(dataServiceType);
return {
update: rxMethod<Entity>(
// details in article
),
// ... the rest of the methods
}
}
)
}
For a more powerful entity based approach, there is withDataService from the ngrx-toolkit. It is based around withEntities
and can add the collection name to the respective state/methods. However, it uses promises instead of observables. I had a PR to extend it to support observables that you could pull pieces from as needed.
Between the article's basics about a signalStoreFeature
that is observable CRUD based and withDataService
's advanced source code, you could piece together something really nice. That's what I did.