web-dev-qa-db-fra.com

Comment sélectionner un seul objet par id en utilisant @ ngrx / entity

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?

9
vachee

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))
);
6
Jordan Gray

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.

1
markoffden

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]
);
1
Colo Ghidini