Angularfire: ForkJoin of observables remain cold

Created on 15 Oct 2016  路  7Comments  路  Source: angular/angularfire

Version info

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

Steps to reproduce

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;
              });
            })
          );
       });

Expected behavior

The expected behavior is that I can subscribe to the resulting Observable and do things.

Actual behavior

The resulting Observable remains cold, it doesn't trigger anything inside the subscribe

Most helpful comment

@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.

All 7 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jteplitz picture jteplitz  路  3Comments

avanderbergh picture avanderbergh  路  3Comments

jnupeter picture jnupeter  路  3Comments

martinyoussef picture martinyoussef  路  3Comments

itisparas picture itisparas  路  3Comments