Observable fires only once
Observable fires only once
I decided to pick up this RxJS tutorial over the weekend to learn about reactive programming. The goal is to set up a simple page that generates an interactive list of users from the Github users API using Observables.
The list displayed is a subset of the total number of users retrieved (in my case 3 out of 30). The list needs to be refreshable (show a new set of users), and you need to be able to remove entries from it by clicking the 'remove' button on each respective entry.
I've set up a chain of Observables to define the behavior of the page. Some act as events to trigger processing, and some publish processed results for use in the app. This chain should dynamically cause my list to be updated. Currently, the default flow is like this:
A list update is triggered on startup, by refreshing the list and by removing something from the list. However, when I refresh the list, this happens:
As you can see, the trigger to update the list of users is not set off. As I understand it, by emitting some value at the start of a stream, the rest of the stream should be executed consequently. However, this only seems to be happening the first time I run through the chain. What am I missing?
You can find a running version of my project here.
1 Answer
1
I think the issue is in the way userStream$
Observable is created.
userStream$
Filtering users not closed and then taking the first 3 is something that can be done directly on the UserModel array passed into the pipe chain by displayEvents$
via filter
and slice
methods of Array.
displayEvents$
filter
slice
If you do so, you remove the need of using the from
function to create an Observable<UserModel>
on which you then have to apply flatMap
(which is currently better known as mergeMap
) to apply finally toArray
to transform it back into an Array of UserModel.
from
Observable<UserModel>
flatMap
mergeMap
toArray
In other words you can simplify the code as in the following example, which as side effect solves the refresh problem.
this.userStream$ = this.displayEvent$.pipe(
map(users => users
.filter((user: UserModel) => !this.closedUsers.has(user))
.slice(0, this.numberOfUsers))
// flatMap((users: UserModel) => from(users))
// // Don't include users we've previously closed.
// , filter((user: UserModel) => !this.closedUsers.has(user))
// , take(this.numberOfUsers)
// , toArray()
, tap(() => console.log('List updated!'))
// Unless we explicitly want to recalculate the list of users, re-use the current result.
, shareReplay(1));
To be honest though I have not fully grasped why your original solution, which is a sort of long detour, does not work.
Yes, this sounds like the root reason. Thx for the explanation
– Picci
Jul 2 at 19:27
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Thanks a lot! This also works for me. I suspect the problem was my use of the take() operator. I set it to take as many users as I wanted to show on the page initially. However, this also means that any subsequent emissions are ignored. I tried adding take(1) to your version of the code, and the same problem popped up again.
– thijsfranck
Jul 2 at 19:04