Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular router navigation inside NgRx effect

Does the Angular router have any restrictions to be used inside an NgRx effect?

I just started learning NgRx and I have the following code:

@Effect() public authenticate$ = this.actions$
    .ofType(authenticationActions.AUTHENTICATE)
        .switchMap((action: AuthenticateAction) => this.authenticationService.authenticate(action.payload)
            .map((data: TokenData) => {
                const user: User = {
                    token: data.token,
                    username: 'dummy',
                };
                console.log(data);
                this.router.navigateByUrl('/');
                return new authenticationActions.AuthenticateSuccessAction(user);
            })
            .catch(error => { console.log(error); return Observable.throw(error); })
        );

The console logs the data variable and the AuthenticateSuccessAction action is being triggered, so the router line is being executed but the navigation doesn't happen.

like image 733
rbasniak Avatar asked May 28 '18 12:05

rbasniak


Video Answer


3 Answers

@Effect() public authenticate$ = this.actions$.pipe(
    ofType(authenticationActions.AUTHENTICATE),
     map(action => action.payload),
    exhaustMap((auth: any) => 
      this.authenticationService.authenticate(auth)
        .map((data: TokenData) => {
            return user: User = {
                token: data.token,
                username: 'dummy',
            };
        }).catch(error => { console.log(error); return Observable.throw(error); 
       }).pipe(
          map(user =>new authenticationActions.AuthenticateSuccessAction(user))
        )
    );)

  @Effect({ dispatch: false })
   loginSuccess$ = this.actions$.pipe(
     ofType(authenticationActions.AuthenticateSuccessAction),
     tap(() => this.router.navigate(['/']))
   );

Use exhaustMap and when you dispatching 'AuthenticateSuccessAction' action, do another effect for redirecting.

Personally, I like to separate all the services from effects, then you can use catchError() operator after success login for dispatching another action in case of failure login.

hope this works. PS: I did not verify this answer but logic is like this.

like image 180
m.akbari Avatar answered Oct 13 '22 12:10

m.akbari


There should be an approach allowing not to create separate effect for redirect, something like

this.actions$.pipe(
  ofType(authenticationActions.AUTHENTICATE),
  switchMap(action =>
    this.authenticationService.authenticate(action.payload).pipe(
      map(data => new authenticationActions.successAction(data)),
      tap(() => this.router.navigate(['/'])),
      catchError(error => new authenticationActions.failAction(error))
    )
);

The thing is that tap will not be invoked if the service call failed, both map and tap will be skipped in favor of catchError if the failure case.

like image 29
dhilt Avatar answered Oct 13 '22 12:10

dhilt


following the response of m.akbari, in the case they use the actions differently, the "dispatch : false" goes to the last.

the key is the "dispatch : false", if this is not indicated an infinite loop is generated

 loginNavigate$ = createEffect(
    () => this.actions$.pipe(
        ofType(usuariosActions.loginUsuarioSuccess),
        tap(() => {
            console.log('iniciando la navegacion');

            this.router.navigate(['/']);
        })
    ), { dispatch: false }
);
like image 39
Carlos Javier Bazan Human Avatar answered Oct 13 '22 13:10

Carlos Javier Bazan Human