Angular: 2.0.0
Firebase: 3.4.1
AngularFire: 2.0.0-beta.5
Other (e.g. Node, browser, operating system) (if applicable):
Node 6.0.0
Windows 10
Any browser
In any flat database, it's common to have to perform multiple queries to complete the object information.
For example, if I have a conversation node with a list of userIds, I would like to perform a query to the node users to retrieve the user information and then create my custom conversation object with all the data.
The problem is that, unlike normal HTTP calls to a REST API, when using a forkJoin with FirebaseObjectObservable or FirebaseListObservable elements, the resulting Observable of the forkJoin remains cold.
Perform any forkjoin of observables, like:
this.af.database.list(`users/${user.id}/conversations`)
.flatMap((conversations) => {
return Observable.forkJoin(
conversations.map((conversation) => {
return Observable.forkJoin(
this.af.database.object(`users/${conversation.receiverId}`),
this.af.database.list(`messages/${conversation.$key}`)
).map((data)=> {
conversation.destinationUser = data[0];
conversation.messages = data[1];
return conversation;
});
})
);
});
The expected behavior is that I can subscribe to the resulting Observable and do things.
The resulting Observable remains cold, it doesn't trigger anything inside the subscribe
Hi,
just to confirm this - I ran into a similar problem:
Observable.forkJoin(
loadSomething(),
loadSomethingElse()
).subscribe(res => {
console.log('ready', res);
});
-> my log never triggers
Note: loadSomething() and loadSomethingElse() are just placeholders for my real functions. They both return a FirebaseListObservable and when I subscribe to them individually they work totally fine as expected.
PS: I'm doing this inside a Ionic 2 RC1 application
Cordova CLI: 6.3.1
Ionic Framework Version: 2.0.0-rc.1
Ionic CLI Version: 2.1.1
Ionic App Lib Version: 2.1.1
Ionic App Scripts Version: 0.0.36
OS: Windows 7 SP1
Node Version: v4.6.0
@Jeffarese @janein I don't belive this is a bug. forkJoin joins the observables when they complete and a FirebaseListObservable or FirebaseObjectObservable doesn't complete.
If you use the first operator to complete the observable after the first emitted value, the forkJoin call should behave in the manner you expect.
The RxJS 5 documentation for forkJoin is non-existant, but the documentation for the previous version does mention this:
Runs all observable sequences in parallel and collect their last elements.
Closing due to @cartant's explanation.
I have tried to use first() in the forkJoin that did not work. Using take(1) did work however. Am I using the code incorrectly?
does not work
Observable.forkJoin(
this.af.database.list('/rooms').first(1),
this.af.database.list('/devices').first(1));
works
Observable.forkJoin(
this.af.database.list('/rooms').take(1),
this.af.database.list('/devices').take(1));
@jusefb I believe it should be .first() with no arguments passed. With that being said, in my use case both .first() and take(1) seemed to work equivalently
@cartant is there a way to have forkJoin emit a new event when the underlying Observables change due to changes to the Firebase database, so that the UI template can update automatically.
I place this in the constructor for a component and it fires when the page loads
Observable.forkJoin(
this.dataService.getFriends(userId, AppSettings.REQUEST_QUERY_USER_ID).first(),
this.dataService.getFriends(userId, AppSettings.REQUEST_QUERY_FRIEND_ID).first()
).subscribe(friends => {
console.log("Friends: ", friends);
this.friends = friends;
});
but if I delete a friend while on the page, Firebase correctly updates, but subscribe doesn't fire again. If I subscribe to something more simple
this.af.database.list('items').subscribe(items => this.items = items);
then changes to Firebase are automatically reflected in my UI
@afreix No, that's not how forkJoin works. It's purpose is to wait until all of the observables passed to it have completed and have emitted at least one value.
It you want an observable that combines the the values and continues to listen, you should use combineLatest, which emits the latest values from each observable - once each has emitted at least one value - and continues to emit the combined values whenever an observable re-emits. And you probably don't want to use first if you are using combineLatest.
Most helpful comment
@afreix No, that's not how
forkJoinworks. It's purpose is to wait until all of the observables passed to it have completed and have emitted at least one value.It you want an observable that combines the the values and continues to listen, you should use
combineLatest, which emits the latest values from each observable - once each has emitted at least one value - and continues to emit the combined values whenever an observable re-emits. And you probably don't want to usefirstif you are usingcombineLatest.