I have an ngrx/signalStore that contains fruits as entities. For proper usage of the signalStore, you need to have an id field in the Fruit class, but the API I use retrieves fruits that have a field fruitId. Is there a way to tell the store that it will be the primary key for the class instead of introducing a duplicated field named id?
Here is my store:
export const FruitStore = signalStore(
{ providedIn: 'root' },
withEntities<Fruit>(),
withMethods((
store,
fruitService = inject(FruitService),
) => ({
loadFruits: rxMethod<void>(
pipe(
exhaustMap(() => {
return fruitService.getFruits().pipe(
tapResponse({
next: (fruits) => {
patchState(store, setAllEntities(fruits);
},
error: (error: { message: string }) => {
console.log(error.message);
}
}),
);
}),
)
),
})),
withHooks({
onInit({ loadFruits }) {
loadFruits();
}
}),
);
My Fruit class:
export class Fruit {
fruitId: number;
name: string;
imageURL: string;
}
My current solution is to add a duplicated field to the class (Fruit class comes from the API generated with OpenAPI, so I can not change it). But I'm not fully satisfied with this solution. Do you have any ideas to improve it?
export type WithId<T> = T & { id: number };
Changes in the store:
export const FruitStore = signalStore(
{ providedIn: 'root' },
// Extend Fruit class 'WithId'
withEntities<WithId<Fruit>>(),
withMethods((
store,
fruitService = inject(FruitService),
) => ({
loadFruits: rxMethod<void>(
pipe(
exhaustMap(() => {
return fruitService.getFruits().pipe(
tapResponse({
next: (fruits) => {
// Add the duplicated field
patchState(store, setAllEntities(fruits.map(fruit => ({ ...fruit, id: fruit.fruitId })));
},
error: (error: { message: string }) => {
console.log(error.message);
}
}),
);
}),
)
),
})),
withHooks({
onInit({ loadFruits }) {
loadFruits();
}
}),
);
I went looking for the answer to this exact question, and I found what I feel is a better answer. This might be a newly added capability, but here it is...
const todoConfig = entityConfig({
entity: type<Todo>(),
collection: 'todo', // changes ids, entityMap, and entities to todoIds, todoEntityMap, and todoEntities.
selectId: (todo) => todo.key, // <-- define it once, then reuse it
});
export const TodosStore = signalStore(
withEntities(todoConfig),
withMethods((store) => ({
addTodo(todo: Todo): void {
patchState(store, addEntity(todo, todoConfig));
},
removeTodo(todo: Todo): void {
patchState(store, removeEntity(todo, todoConfig));
},
}))
);