En utilisant @ ngrx/entity, je veux sélectionner une entité par un identifiant unique ou un tableau d'entités par un tableau d'identifiants à partir d'une carte d'entité.
Je ne veux pas que les abonnements sélectionnés à l'intérieur d'un composant soient déclenchés lorsque la collection d'entités obtient un nouvel élément ou qu'un élément d'entité change, ce que je n'ai pas sélectionné du tout.
Cela m'arrive évidemment lorsque j'utilise le sélecteur selectEntities et que je sélectionne ensuite les ID dans le résultat.
Alors, comment puis-je sélectionner 1 ou n éléments par identifiant dans une collection d'entités?
NgRx prend en charge les sélecteurs paramétrés en en passant props
comme dernier argument à une fonction de sélection :
export const selectEntity = createSelector(
selectEntities,
(entities, props) => entities[props.id]
);
export const selectEntitiesByID = createSelector(
selectEntities,
(entities, props) => props.ids.map(id => entities[id])
);
Ceux-ci sont invoqués exactement comme vous pouvez vous y attendre:
this.store.pipe(
select(selectEntity, { id: someID })
);
this.store.pipe(
select(selectEntitiesByID, { ids: arrayOfIDs })
);
Si vos identifiants ne changent pas, vous pouvez les refactoriser en fonctions d'usine :
export const selectEntity = id => createSelector(
selectEntities,
entities => entities[id]
);
export const selectEntitiesByID = ids => createSelector(
selectEntities,
entities => ids.map(id => entities[id])
);
Qui s'appellent ainsi:
this.store.pipe(
select(selectEntity(someID))
);
this.store.pipe(
select(selectEntitiesByID(arrayOfIDs))
);
Pour les deux scénarios, je le gérerais avec un sélecteur dédié:
// single entity
export const singleEntitySelector = createSelector(
// you should have set it up already
yourEntitiesObjSelector,
// here I assume you have set up router reducer state or any other
state slice where you keep single entity id
yourIdSelector,
// then you just return single entity as entities will be an object
(entities, id) => entities[id]
);
// same for array (you will have to store selected ids also on the
state tree)
export const selectedEntitiesArraySelector = createSelector(
// you should have set it up already
yourEntitiesObjSelector,
// here I assume you have set up selected ids store slice
yourSelectedIdsArraySelector,
// then you just return entities array reducing ids array
(entities, idsArray) => idsArray.reduce((acc, id) => {
return entities[id] ? [...acc, entities[id]] : acc;
}, [])
);
Ensuite, vous utiliserez simplement ces sélecteurs dans votre composant, reflétant les changements dans la vue avec le tube async
comme d'habitude. Ils refléteront tous les changements: soit il y a eu un changement d'ID d'entité unique, soit un changement de tableau d'ID. Vous n'avez pas besoin de vous abonner à quoi que ce soit, sauf s'il existe une logique supplémentaire dans votre composant.
Outre entities
et ids
j'ajoute également un selectedEntityId
sur mon état en faisant sur un exemple utilisateur:
import {User} from '../models/user.model';
import {EntityState, createEntityAdapter} from '@ngrx/entity';
export interface UsersState extends EntityState<User> {
// additional entities state properties
selectedUserId: number | null;
}
Et les sélecteurs ressembleraient à ceci:
export const selectUserEntities = selectEntities;
export const getSelectedUserId = (state: UsersState) => state.selectedUserId;
export const selectCurrentUser = createSelector(
selectUserEntities,
getSelectedUserId,
(userEntities, userId) => userEntities[userId]
);