question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Second subscribe on Observable from snapshotChanges() does not emit the current snapshot

See original GitHub issue

Version info

Angular: 5.1.1

Firebase: 5.0.0-rc.4

AngularFire: 5.0.0-rc.5

How to reproduce these conditions

This Stackblitz demonstrates the problem. https://stackblitz.com/edit/angular-keww6x

Steps to set up and reproduce

Create an Observable by calling valueChanges() or snapshotChanges() on a ref in the Firebase Database (eg. const data$ = db.object('data').valueChanges()).

Then asynchronously subscribe twice to that single Observable.

Sample data and security rules

The same database as used in the Stackblitz can be used. Or any other database, as long as there is data in it.

Expected behavior

When subscribing to the Observable, I expect to always get the current value first and all the changes after that.

Actual behavior

The first subscription gets the current value and any changes. The second subscription does not get the first value, only the subsequent changes.

If all the subscriptions are unsubscribed, however, the next subscription gets the initial value again.

Workaround

The current workaround in my code is to surround all the valueChanges() and snapshotChanges() calls with an Observable.defer().

Suspected culprit

The share() operator in fromRef. This operator shares the subscription with all of its subscribers, but will not emit the initial value a second time.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:6
  • Comments:19 (3 by maintainers)

github_iconTop GitHub Comments

7reactions
davideastcommented, Jan 9, 2018

This is a tricky. It’s a hot vs. cold observable situation.

All AngularFire observables are hot, meaning they values are emitted live. A second subscribe will not emit the “original” value. This is the intended design.

Why are AngularFire observables hot? A Firebase/Firestore reference is created outside the observable, which by default makes it hot. I’ll be citing @BenLesh’s excellent article to explain.

Below is an example of a cold observable:

// Pseduo-code
const cold = new Observable((observer) => {
  const ref = new Reference();
  const unsubscribe = ref.onSnapshot(observer);
  return { unsubscribe };
});

This would create a new reference for each subscribe. Which is not ideal. Instead we create the reference outside of the observable, which makes the observable hot.

// Pseduo-code
const ref = new Reference();
const hot = new Observable((observer) => {
  const unsubscribe = ref.onSnapshot(observer);
  return { unsubscribe };
});

The kink in this situation is that methods like .on('child_added') or onSnapshot() work in a cold like manner. Meaning each time you call them they will replay the same results in the callback.

Ideally, you wouldn’t run into this situation because subscribe() should be called just once. However, I am willing to make a change to have these observables act in a cold like manner to match the Firebase SDK.

I’m working on a few infrastructure changes with our typings, but after that I will get this change released.

6reactions
sgarciaccommented, Aug 13, 2018

I’m using the operator shareReplay on snapshotChanges() to be able to subscribe several times.

Read more comments on GitHub >

github_iconTop Results From Across the Web

AngularFirestoreCollection snapshotChanges() does not emit ...
Try subscribing to this.AppUsers. this.AppUsers = this.AppUserCollection.snapshotChanges() .map(actions => { return actions.map(a => { const ...
Read more >
take - Learn RxJS
Another use-case is when you need to take a snapshot of data at a particular point in time but do not require further...
Read more >
Get realtime updates with Cloud Firestore - Firebase
An initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document. Then, each...
Read more >
rxjs take unsubscribe - You.com | The search engine you control.
Unsubscribing Declaratively with takeUntil. The solution is to compose the subscriptions with the takeUntil operator and use a subject that emits a truthy...
Read more >
How to cause observables to re-emit when ngModel value ...
It will get triggered every time there are changes in ngModel. The $event payload will hold the current value of the form field....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found