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!
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');