@Injectable()
export class UserEffects {
constructor(
private store$: Store<fromRoot.State>,
private actions$: Actions,
private router: Router,
private chatService: ChatService,
private authService: AuthService,
private db: AngularFireAuth,
private http: HttpClient
) { }
@Effect()
login$: Observable<Action> = this.actions$.pipe(
ofType(UserActions.LOGIN),
switchMap((action: UserActions.LoginAction) =>
from(this.db.auth.signInWithEmailAndPassword(action.payload.email, action.payload.password)),
),
// switchMapTo(from(this.db.auth.currentUser.getIdToken())),
switchMapTo(from(this.db.authState.pipe(
take(1),
switchMap((user)=>{
if (user)
return from(user.getIdToken());
else
return of(null);
})
))),
switchMap(token => this.http.post(environment.apiUrl + '/api/login', { token: token })),
tap((response: any) => {
if (response.valid === 'true') {
localStorage.setItem('token', response.token);
this.router.navigate(['dash']);
}
}),
map(response => new UserActions.LoginSuccessAction({ response })),
catchError(error => {
return of({
type: UserActions.LOGIN_FAILURE,
payload: error
});
})
);
@Effect({ dispatch: false })
loginSuccess$: Observable<Action> = this.actions$.pipe(
ofType(UserActions.LOGIN_SUCCESS),
tap((action: UserActions.LoginSuccessAction) => {
console.log("LOGIN_SUCCESS");
console.log(action);
}));
@Effect({ dispatch: false })
loginFailure$: Observable<Action> = this.actions$.pipe(
ofType(UserActions.LOGIN_FAILURE),
tap((action: UserActions.LoginFailureAction)=>{
console.log("LOGIN_FAILURE");
console.log(action.payload.code);
}));
}
Use case: You are at a login page. You enter your username and password to call LoginAction. If all goes well, LoginSuccessAction should be called. Otherwise, LoginFailureAction should be called.
Everything works except one case:
Once LoginFailureAction is called once, neither LoginSuccessAction nor LoginFailureAction will be called again. I am at a loss of why this is happening. I think it may have something to do with the line:
catchError(error => { return of({ type: UserActions.LOGIN_FAILURE, payload: error }); })
but here I am lost. I am not an expert on reactive programming, but it all works as it's supposed to except this one case. Does anyone have an idea why this occurs?
Effects respect successful completion and errors. In case of an error ngrx will resubscribe, in case case of a successful completion it will not.
For example catchError
can cause this case, because it doesn't listen to its parent stream. If you want to enable stream in case of an error you need to use repeat
operator right after catchError
.
catchError(error => {
return of({
type: UserActions.LOGIN_FAILURE,
payload: error
});
}),
repeat(), // <- now effect is stable.