Second subscribe on Observable from snapshotChanges() does not emit the current snapshot
See original GitHub issueVersion 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:
- Created 6 years ago
- Reactions:6
- Comments:19 (3 by maintainers)
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:
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.
The kink in this situation is that methods like
.on('child_added')
oronSnapshot()
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.
I’m using the operator shareReplay on snapshotChanges() to be able to subscribe several times.