Skip to content

Commit 7fb596e

Browse files
committed
fix(auth): add scheduler to schedule onAuth events through Angular zone
Closes #354
1 parent a426aac commit 7fb596e

File tree

5 files changed

+62
-6
lines changed

5 files changed

+62
-6
lines changed

src/auth/auth.spec.ts

+23
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,29 @@ const AngularFireAuthState = <FirebaseAuthState>{
8787
}
8888
};
8989

90+
describe('Zones', () => {
91+
it('should call operators and subscriber in the same zone as when service was initialized', (done) => {
92+
// Initialize the app outside of the zone, to mimick real life behavior.
93+
var app = initializeApp(COMMON_CONFIG, 'zoneapp');
94+
95+
let ngZone = Zone.current.fork({
96+
name: 'ngZone'
97+
});
98+
ngZone.run(() => {
99+
var afAuth = new AngularFireAuth(new FirebaseSdkAuthBackend(app), window.location);
100+
afAuth
101+
.take(1)
102+
.do(_ => {
103+
expect(Zone.current.name).toBe('ngZone');
104+
})
105+
.subscribe(() => {
106+
expect(Zone.current.name).toBe('ngZone');
107+
done()
108+
}, done.fail);
109+
});
110+
});
111+
});
112+
90113
describe('FirebaseAuth', () => {
91114
let app: firebase.app.App;
92115
let authData: any;

src/auth/firebase_sdk_auth_backend.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core';
22
import { Observable } from 'rxjs/Observable';
33
import { Observer } from 'rxjs/Observer';
44
import { FirebaseApp } from '../tokens';
5-
import { isPresent } from '../utils';
5+
import { isPresent, ZoneScheduler } from '../utils';
66
import { auth } from 'firebase';
77
import {
88
authDataToAuthState,
@@ -22,6 +22,7 @@ const {
2222

2323
import 'rxjs/add/operator/map';
2424
import 'rxjs/add/observable/fromPromise';
25+
import 'rxjs/add/operator/observeOn';
2526

2627
@Injectable()
2728
export class FirebaseSdkAuthBackend extends AuthBackend {
@@ -42,14 +43,20 @@ export class FirebaseSdkAuthBackend extends AuthBackend {
4243
}
4344

4445
onAuth(): Observable<FirebaseAuthState> {
45-
// TODO: assumes this will accept an RxJS observer
4646
return Observable.create((observer: Observer<FirebaseAuthState>) => {
4747
return this._fbAuth.onAuthStateChanged(observer);
4848
})
4949
.map((user: firebase.User) => {
5050
if (!user) return null;
5151
return authDataToAuthState(user);
52-
});
52+
})
53+
/**
54+
* TODO: since the auth service automatically subscribes to this before
55+
* any user, it will run in the Angular zone, instead of the subscription
56+
* zone. The auth service should be refactored to capture the subscription
57+
* zone and not use a ReplaySubject.
58+
**/
59+
.observeOn(new ZoneScheduler(Zone.current));
5360
}
5461

5562
unauth(): void {

src/utils.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { Subscription } from 'rxjs/Subscription';
2+
import { QueueScheduler } from 'rxjs/scheduler/QueueScheduler';
3+
import { Scheduler } from 'rxjs/Scheduler';
14
import { AFUnwrappedDataSnapshot} from './interfaces';
25

36
export function isPresent(obj: any): boolean {
@@ -75,4 +78,21 @@ export function stripLeadingSlash(value: string): string {
7578
} else {
7679
return value;
7780
}
78-
}
81+
}
82+
83+
/**
84+
* TODO: remove this scheduler once Rx has a more robust story for working
85+
* with zones.
86+
*/
87+
export class ZoneScheduler extends QueueScheduler {
88+
constructor(public zone?: Zone) {
89+
super();
90+
}
91+
92+
schedule(...args): Subscription {
93+
if (!this.zone) {
94+
throw new Error('ZoneScheduler.schedule was called before a zone was set');
95+
}
96+
return <Subscription>this.zone.run(() => super.schedule.apply(this, args));
97+
}
98+
}

test/e2e/auth/firebase_auth_example.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
FirebaseApp
1212
} from '../../../dist/angularfire2';
1313

14+
declare var Zone: any;
15+
1416
enableProdMode();
1517

1618
// TODO fix imports and tsconfig
@@ -111,7 +113,10 @@ class App {
111113
auth: null // makes easier to convert to json
112114
})
113115
})
114-
.subscribe(user => this.user = user);
116+
.subscribe(user => {
117+
console.log('zone', Zone.current.name);
118+
this.user = user
119+
});
115120
}
116121

117122
signInAnonymously() {

tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"src/auth/auth.spec.ts",
2626
"src/auth/auth_backend.spec.ts",
2727
"typings/index.d.ts",
28-
"manual_typings/firebase3/firebase3.d.ts"
28+
"manual_typings/firebase3/firebase3.d.ts",
29+
"node_modules/zone.js/dist/zone.js.d.ts"
2930
]
3031
}

0 commit comments

Comments
 (0)