J'ai un ensemble statique de données, une liste de pays, qui sont utilisés sur certains composants. Ces données sont chargées sur la ngOnInit()
de ces composants mais je voudrais les charger uniquement si c'est la toute première fois que je demande les données (le magasin est vide). Chaque fois que je charge le composant, j'aimerais simplement utiliser les données du magasin sans les "rafraîchir".
Comment est-ce réalisable en utilisant ngrx?
J'utilise des effets. Voici à quoi ressemble mon code:
Le composant:
export class EditPageComponent implements OnInit {
countries$: Observable<Country[]>
constructor(private store: Store<fromContacts.State>) {
this.countries$ = store.select(fromContacts.getCountriesEntities);
}
ngOnInit() {
this.store.dispatch(new countries.Load());
}
L'effet:
@Effect()
loadCollection$: Observable<Action> = this.actions$
.ofType(countries.LOAD)
.switchMap(() =>
this.countriesService
.getCountries()
.map((countriesList: Country[]) => {
return new countries.LoadSuccess(countriesList);
})
.catch(error => of(new countries.LoadFail(error)))
);
Et le réducteur:
case countries.LOAD_SUCCESS: {
const countriesList: Country[] = action.payload;
const reducedCountries: { [id: string]: Country } = countriesList.reduce((countrs: { [id: string]: Country }, countr: Country) => {
return Object.assign(countrs, {
[countr.code]: countr
});
}, {});
Merci, Gab
Il existe différentes façons de procéder. Tout d'abord, vous pouvez garder un hasLoaded: boolean
propriété en état. Ensuite, vous pouvez vérifier cela avant de faire appel au service.
ngOnInit() {
this.store.select(getHasLoaded)
.take(1)
.subscribe(hasLoaded => {
if (!hasLoaded) this.store.dispatch(new countries.Load());
}
}
Une autre option consiste à laisser votre @Effect vérifier la propriété hasLoaded:
@Effect()
loadCollection$: Observable<Action> = this.actions$
.ofType(countries.LOAD)
.withLatestFrom(this.store.select(getHasLoaded)
.filter(([ action, hasLoaded ]) => !hasLoaded) // only continue if hasLoaded is false
.switchMap(() =>
this.countriesService
.getCountries()
.map((countriesList: Country[]) => {
return new countries.LoadSuccess(countriesList);
})
.catch(error => of(new countries.LoadFail(error)))
);
Pour que cela fonctionne, vous devez fournir le magasin dans votre constructeur d'effets.
take(1)
dans l'effetN'oubliez pas la gestion des erreurs en utilisant
catchError
et retournezEMPTY
, sinon lorsqu'une erreur se produit, cette erreur sera toujours renvoyée (timeout, erreur d'authentification, hors ligne ...)
J'ai eu exactement le même cas que vous, ce que j'ai fait a été d'ajouter dans les effets l'opérateur rxjs take
pour récupérer les pays uniquement la première fois que l'action LoadCountries
a été envoyée.
@Effect()
loadCountries$: Observable<CoreActions> = this.actions$.pipe(
ofType(CoreActionTypes.LoadCountries),
mergeMap(() =>
this.countriesService.getAllCountries().pipe(
map(c => new LoadCountriesSuccess(c)),
catchError(() => {
this.store.dispatch(new LoadCountriesFailed());
return EMPTY;
})
)
),
take(1)
);
Retourner EMPTY
à l'intérieur catchError
terminera l'observable sans passer par la take(1)
Vous pouvez sélectionner les pays du magasin dans les effets, s'ils sont représentés dans le magasin, nous ignorons l'action, sinon nous récupérons les pays.
@Effect()
getOrder = this.actions.pipe(
ofType<GetOrder>(ActionTypes.GetOrder),
withLatestFrom(this.store.pipe(select(getOrders))),
filter(([{payload}, orders]) => !!orders[payload.orderId])
mergeMap([{payload}] => {
...
})
)
Pour plus d'informations, voir Commencez à utiliser ngrx/effects pour cela .
Ma réponse est une variation de Hetty de Vries
réponses. Si vous souhaitez simplement récupérer le contenu de la boutique sans toucher aux effets, vous pouvez faire quelque chose de similaire à ceci:
this.store.select(state => state.producer.id).forEach( id => {
...
}
en supposant que votre magasin contient un objet producer
avec un attribut id
.