J'ai une question très basique concernant les effets @ngrx: comment ignorer une erreur qui survient pendant l'exécution d'un effet de telle sorte qu'elle n'affecte pas l'exécution ultérieure de l'effet?
Ma situation est la suivante: j'ai une action (LOGIN) et un effet qui écoute cette action. Si une erreur survient à l'intérieur de cet effet, je veux l'ignorer. Lorsque LOGIN est envoyé une seconde fois après cette erreur, l'effet doit être exécuté une seconde fois.
Ma première tentative a été:
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.flatMap(async () => {
console.debug('LOGIN');
// throw an error
let x = [];x[0]();
})
.catch(err => {
console.error('Error at login', err);
return Observable.empty();
});
Envoi de LOGIN pour la première fois, lève et intercepte l'erreur, comme prévu. Cependant, si je répète LOGIN une deuxième fois par la suite, rien ne se passe; l'effet n'est pas exécuté.
J'ai donc essayé ce qui suit:
.catch(err => {
return this.login$;
});
, mais cela aboutit à une boucle sans fin ... Savez-vous comment capter l'erreur sans empêcher plus tard l'exécution de l'effet?
L'infrastructure ngrx
souscrit à l'effet via le fournisseur importé dans la variable NgModule
de l'application à l'aide de EffectsModule.run
.
Lorsque les erreurs observables et catch
renvoie un observable vide, l'observable composé est terminé et ses abonnés ne sont pas abonnés - cela fait partie du Observable Contract . Et c’est la raison pour laquelle vous ne voyez plus aucun traitement des actions LOGIN
dans votre effet. L'effet est désabonné à la fin et l'infrastructure ngrx
ne se réabonne pas.
En règle générale, la gestion des erreurs dans la flatMap
(maintenant appelée mergeMap
) est la suivante:
import { Actions, Effect, toPayload } from "@ngrx/effects";
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.map(toPayload)
.flatMap(payload => Observable
.from(Promise.reject('Boom!'))
.catch(error => {
console.error('Error at login', error);
return Observable.empty();
})
});
Le catch
composé dans l'observable interne verra un observable vide aplati/fusionné dans l'effet, ainsi aucune action ne sera émise.
Le flux @Effect
est complétant lorsque l'erreur se produit, empêchant toute action ultérieure.
La solution consiste à passer à un flux jetable. Si une erreur survient dans le flux disponible, c'est correct, car le flux principal @Effect
reste toujours actif et les actions futures continuent de s'exécuter.
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.switchMap(action => {
// This is the disposable stream!
// Errors can safely occur in here without killing the original stream
return Rx.Observable.of(action)
.map(action => {
// Code here that throws an error
})
.catch(error => {
// You could also return an 'Error' action here instead
return Observable.empty();
});
});
Plus d'informations sur cette technique dans cet article de blog: La quête de boulettes géantes: continuez les flux RxJS lorsque des erreurs se produisent
voici comment je gère l'option d'être notifié ou non sur Observables qui n'a pas trouvé de données:
public listenCampaignValueChanged(emitOnEmpty: boolean = false): Observable<CampaignsModelExt> {
var campaignIdSelected$ = this.ngrxStore.select(store => store.appDb.uiState.campaign.campaignSelected)
var campaigns$ = this.ngrxStore.select(store => store.msDatabase.sdk.table_campaigns);
return campaignIdSelected$
.combineLatest(campaigns$, (campaignId: number, campaigns: List<CampaignsModelExt>) => {
return campaigns.find((i_campaign: CampaignsModelExt) => {
return i_campaign.getCampaignId() == campaignId;
});
}).flatMap(v => (v ? Observable.of(v) : ( emitOnEmpty ? Observable.of(v) : Observable.empty())));
}