Reputation: 452
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!
Upvotes: 3
Views: 2537
Reputation: 452
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)))
);
})
);
Upvotes: 0
Reputation: 8269
Can you try this code and see if it works fine. Have added tap operator to see if such changes/data emitted from the store took place.
this.userRole$ = this.store
.select(selectUserProfile)
.pipe(
tap(user => console.log('FETCHED:', user)), // Console if user data exists
filter(Boolean), // Change !!user to Boolean
tap(user => console.log('FILTERED:', user)), // Console if user exists after filter
map(user => parseUserRole(user.role))
tap(user => console.log('MAPPED:', user)), // Console if user is mapped
);
NOTE:
That the store will only send it's response once the prior/parent service is invoked.
Example:
// If along with the getUsers() functionality has a piped storeService in it
// http.get(url).pipe(tap(data => this.storeService.set('users', data)))
this.userService.getUsers().subscribe();
// You can then initialize your store to get the data invoked/set above
this.user$ = this.store.select('users');
Upvotes: 1