Skip to content

Commit e18da0e

Browse files
committed
fix(Database): use Zone scheduler for object and list factories
Since Firebase is adding WebSocket listeners before the Angular Zone exists, all callbacks when data is received are being executed outside of Angular's zone and are not triggering change detection. This fix uses the existing ZoneScheduler, which is already used in auth.onAuth, to cause the observables created by af.list() and af.object() to be emitted inside the current Zone at the time of calling .list() or .object(). See angular/angular#4603 for progress on making RxJS more zone-aware. Fixes #637
1 parent 2c0a57f commit e18da0e

4 files changed

+48
-2
lines changed

src/database/firebase_list_factory.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,26 @@ describe('FirebaseListFactory', () => {
541541
});
542542
});
543543

544+
it('should emit values in the observable creation zone', (done: any) => {
545+
Zone.current.fork({
546+
name: 'newZone'
547+
})
548+
.run(() => {
549+
// Creating a new observable so that the current zone is captured.
550+
subscription = FirebaseListFactory(`${rootDatabaseUrl}/questions`)
551+
.filter(d => d
552+
.map(v => v.$value)
553+
.indexOf('in-the-zone') > -1)
554+
.subscribe(data => {
555+
expect(Zone.current.name).toBe('newZone');
556+
done();
557+
});
558+
});
559+
560+
ref.remove(() => {
561+
ref.push('in-the-zone');
562+
});
563+
});
544564
});
545565
});
546566

src/database/firebase_list_factory.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as firebase from 'firebase';
22
import { AFUnwrappedDataSnapshot } from '../interfaces';
33
import { FirebaseListObservable } from './firebase_list_observable';
44
import { Observer } from 'rxjs/Observer';
5+
import { observeOn } from 'rxjs/operator/observeOn';
56
import { observeQuery } from './query_observable';
67
import { Query, FirebaseListFactoryOpts } from '../interfaces';
78
import * as utils from '../utils';
@@ -152,7 +153,9 @@ function firebaseListObservable(ref: firebase.database.Reference | firebase.data
152153
ref.off('child_changed', chgFn);
153154
}
154155
});
155-
return listObs;
156+
157+
// TODO: should be in the subscription zone instead
158+
return observeOn.call(listObs, new utils.ZoneScheduler(Zone.current));
156159
}
157160

158161
export function onChildAdded(arr:any[], child:any, prevKey:string): any[] {

src/database/firebase_object_factory.spec.ts

+19
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,24 @@ describe('FirebaseObjectFactory', () => {
136136
subscription.unsubscribe();
137137
expect(firebaseSpy).toHaveBeenCalled();
138138
});
139+
140+
it('should emit values in the observable creation zone', (done: any) => {
141+
Zone.current.fork({
142+
name: 'newZone'
143+
})
144+
.run(() => {
145+
// Creating a new observable so that the current zone is captured.
146+
subscription = FirebaseObjectFactory(`${rootDatabaseUrl}/questions/${i}`)
147+
.filter(d => d.$value === 'in-the-zone')
148+
.subscribe(data => {
149+
expect(Zone.current.name).toBe('newZone');
150+
done();
151+
});
152+
});
153+
154+
ref.remove(() => {
155+
ref.set('in-the-zone');
156+
});
157+
});
139158
});
140159
});

src/database/firebase_object_factory.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FirebaseObjectObservable } from './index';
22
import { Observer } from 'rxjs/Observer';
3+
import { observeOn } from 'rxjs/operator/observeOn';
34
import * as firebase from 'firebase';
45
import * as utils from '../utils';
56
import { Query } from '../interfaces';
@@ -17,7 +18,7 @@ export function FirebaseObjectFactory (
1718
isRef: () => ref = <firebase.database.Reference>absoluteUrlOrDbRef
1819
});
1920

20-
return new FirebaseObjectObservable((obs: Observer<any>) => {
21+
const objectObservable = new FirebaseObjectObservable((obs: Observer<any>) => {
2122
let fn = ref.on('value', (snapshot: firebase.database.DataSnapshot) => {
2223
obs.next(preserveSnapshot ? snapshot : utils.unwrapMapFn(snapshot))
2324
}, err => {
@@ -26,4 +27,7 @@ export function FirebaseObjectFactory (
2627

2728
return () => ref.off('value', fn);
2829
}, ref);
30+
31+
// TODO: should be in the subscription zone instead
32+
return observeOn.call(objectObservable, new utils.ZoneScheduler(Zone.current));
2933
}

0 commit comments

Comments
 (0)