angularrxjsngrxasync-pipe

Why doesn't async pipe detect changes in Angular (but subscription in component.ts does)?


I'm using an async pipe in angular to monitor an observable coming from an rxjs store. The component's ngOnInit looks like this

ngOnInit() {
  this.userRole$ = this.store.select(selectUserProfile).pipe(
    filter(user => !!user),
    map(user => parseUserRole(user.role))
  );
}

The component template looks like this

<p> User role is {{ userRole$ | async }} </p>

After the user logs in, the message on the page remains an empty string

In the component.ts I added the following the code to debug the issue

this.userRole$.subscribe(userRole => {
  alert(`new user role is ${userRole}`);
});

After the user logs in, I get an alert saying "new user role is admin", however, the pipe in the html template doesn't update.

When I replaced the store select() with a dummy value using of() and everything worked as expected, so I'm pretty sure the problem is something to do with rxjs.

The auth reducer (which is firing) looks like this (

export function reducer(state = initialAuthState, action: AuthActions): AuthState {
    switch (action.type) {
        /// ...CODE OMITTED
        case AuthActionTypes.UserProfileRetrieved:
            alert(`setting user profile ${action.payload.userProfile}`)
            return { ...state, userProfile: action.payload.userProfile }
        default:
            return state;
    }
}

I've tried making sure the UserProfileRetrieved action is dispatched from inside ngZone.run() but that didn't make difference. I don't really know how else to go about debugging this. Any pointers would be greatly appreciated. Thank you!


Solution

  • This turned out to be caused by a side-effect of the auth0 native app login flow. The app router was configured to use hash-based routing in order to support Cordova. After getting the login token from auth0, we were clearing the hash to remove the token information. Any store updates after this point were not being reflected in the async pipes, but updates before this were.

    If anyone is having a similar problem make sure that clearing the hash is the last thing you do before navigating to the post authorization URL. Thanks @Shorbagy and @KShewengger for your help.

    Here's the code I used to do this

    @Effect()
        loginComplete$ = this.actions$.pipe(
            ofType<LoginComplete>(AuthActionTypes.LoginComplete),
            exhaustMap(() => {
                return this.authService.authResult$.pipe(
                    map((authResult: any) => {
                        if (environment.platform.name === Platforms.BROWSER) {
                            window.location.hash = '';
                        }
    
                        if (authResult && authResult.accessToken) {
                            this.authService.setAuth(authResult);
                            return new LoginSuccess();
                        } else {
                            debugger;
                            return new LoginFailure({ message: 'no accessToken', payload: authResult })
                        }
                    }),
                    catchError(error => of(new LoginFailure(error)))
                );
            })
        );