Skip to content

Commit a53fac0

Browse files
katowulfdavideast
katowulf
authored andcommitted
fix(database): Add $ref to observables (#447)
* feat(FirebaseObjectObservable, FirebaseArrayObservable): Add $ref to observables Makes the firebase.database.Reference used to create the observable public so that it can be used for accessing child records and paths relative to this collection/object. This closes #294 * Locked zone.js to 0.6.12 per bug angular/zone.js#404, bug #468 created to address this. Commented test units to correct `error TS2339`; bug #467 created to address this.
1 parent 133b908 commit a53fac0

7 files changed

+109
-30
lines changed

docs/api-reference.md

+62-3
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,72 @@ class App {
225225
}
226226
227227
```
228+
228229
### FirebaseListObservable
229230

230231
Subclass of rxjs `Observable` which also has methods for updating
231232
list-like Firebase data.
232233

233-
type: `class`
234+
Type: `class`
235+
236+
Properties:
237+
238+
`$ref:(firebase.database.Reference)`: The reference used to sync this
239+
collection to the Firebase database. See
240+
[firebase.database.Reference](https://firebase.google.com/docs/reference/js/firebase.database.Reference)
241+
242+
Methods:
243+
244+
`push:(val) => Promise`: Add an element to the Firebase Database.
245+
This is the equivalent of the Firebase SDK's
246+
[set() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#set).
247+
See [Saving Data](https://firebase.google.com/docs/database/web/save-data)
248+
for info about restricted characters in object keys and more details about
249+
saving data in the Database.
250+
251+
`update:(item:Object) => void`: Replace any child keys provided in `val`
252+
with the values provided, but do not touch any other keys in the element.
253+
This is the equivalent of the Firebase SDK's
254+
[update() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#update).
255+
256+
`remove:([item]) => void`: Remove an element from the Firebase Database.
257+
If no `item` argument is provided, it removes all elements from the list.
258+
This is the equivalent of the Firebase SDK's
259+
[remove() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove).
260+
261+
### FirebaseObjectObservable
262+
263+
Subclass of rxjs `Observable` which also has methods for syncing and
264+
updating object-like Firebase data. {For collections and lists, see
265+
FirebaseListObservable.)
266+
267+
Type: `class`
268+
269+
Properties:
270+
271+
`$ref:(firebase.database.Reference)`: The reference used to sync
272+
this collection to the Firebase database. See
273+
[firebase.database.Reference](https://firebase.google.com/docs/reference/js/firebase.database.Reference)
274+
275+
Methods:
276+
277+
`set:(val:any) => Promise`: Replaces any data at this path in the Database
278+
with the value provided here (or adds the data if it doesn't exist).
279+
This is the equivalent of the Firebase SDK's
280+
[set() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#set).
281+
See [Saving Data](https://firebase.google.com/docs/database/web/save-data)
282+
for info about restricted characters in object keys and more details about
283+
saving data in the Database.
234284

235-
additional methods:
285+
`update:(val:Object) => void`: Replace any child keys provided in `val`
286+
with the values provided here. The primary difference between this method
287+
and `set()` above, is that `update()` modifies only the keys provided,
288+
leaving any other data untouched, where `set()` essentially replaces
289+
all data at the given path.
290+
This is the equivalent of the Firebase SDK's
291+
[update() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#update).
236292

237-
`add:(val) => void`: Add an element to the Firebase ref.
293+
`remove:() => void`: Remove an element from the Firebase Database.
294+
If no `item` argument is provided, it removes all elements from the list.
295+
This is the equivalent of the Firebase SDK's
296+
[remove() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove).

src/database/firebase_list_factory.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,8 @@ describe('FirebaseListFactory', () => {
364364
firebase.database().ref().remove(done);
365365
questions = FirebaseListFactory(`${rootDatabaseUrl}/questions`);
366366
questionsSnapshotted = FirebaseListFactory(`${rootDatabaseUrl}/questionssnapshot`, { preserveSnapshot: true });
367-
ref = (<any>questions)._ref;
368-
refSnapshotted = (<any>questionsSnapshotted)._ref;
367+
ref = (<any>questions).$ref;
368+
refSnapshotted = (<any>questionsSnapshotted).$ref;
369369
});
370370

371371
afterEach((done: any) => {
@@ -377,7 +377,7 @@ describe('FirebaseListFactory', () => {
377377

378378

379379
it('should emit only when the initial data set has been loaded', (done: any) => {
380-
(<any>questions)._ref.set([{ initial1: true }, { initial2: true }, { initial3: true }, { initial4: true }])
380+
(<any>questions).$ref.set([{ initial1: true }, { initial2: true }, { initial3: true }, { initial4: true }])
381381
.then(() => toPromise.call(skipAndTake(questions, 1)))
382382
.then((val: any[]) => {
383383
expect(val.length).toBe(4);

src/database/firebase_list_observable.spec.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { FirebaseListObservable } from './index';
22
import { Observer } from 'rxjs/Observer';
3-
import { Observable } from 'rxjs/Observable';
43
import { map } from 'rxjs/operator/map';
54
import { database } from 'firebase';
65
import { unwrapMapFn } from '../utils';
@@ -49,6 +48,16 @@ describe('FirebaseObservable', () => {
4948
expect(map.call(O, noop) instanceof FirebaseListObservable).toBe(true);
5049
});
5150

51+
describe('$ref', () => {
52+
// it('should be a firebase.database.Reference', () => {
53+
// expect(O.$ref instanceof database.Reference).toBe(true);
54+
// });
55+
56+
it('should match the database path passed in the constructor', () => {
57+
expect(O.$ref.toString()).toEqual(ref.toString());
58+
});
59+
});
60+
5261
describe('push', () => {
5362
it('should throw an exception if pushed when not subscribed', () => {
5463
O = new FirebaseListObservable(null, (observer:Observer<any>) => {});

src/database/firebase_list_observable.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,40 @@ import { Observable } from 'rxjs/Observable';
22
import { Operator } from 'rxjs/Operator';
33
import { Subscriber } from 'rxjs/Subscriber';
44
import { Subscription } from 'rxjs/Subscription';
5+
import * as firebase from 'firebase';
56
import * as utils from '../utils';
6-
import {
7-
AFUnwrappedDataSnapshot,
8-
FirebaseOperationCases
7+
import {
8+
AFUnwrappedDataSnapshot,
9+
FirebaseOperationCases
910
} from '../interfaces';
1011

1112
export type FirebaseOperation = string | firebase.database.Reference | firebase.database.DataSnapshot | AFUnwrappedDataSnapshot;
1213

1314
export class FirebaseListObservable<T> extends Observable<T> {
14-
constructor(public _ref: firebase.database.Reference | firebase.database.Query, subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void) {
15+
constructor(public $ref: firebase.database.Reference | firebase.database.Query, subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void) {
1516
super(subscribe);
1617
}
1718
lift<T, R>(operator: Operator<T, R>): Observable<R> {
18-
const observable = new FirebaseListObservable<R>(this._ref);
19+
const observable = new FirebaseListObservable<R>(this.$ref);
1920
observable.source = this;
2021
observable.operator = operator;
21-
observable._ref = this._ref;
22+
observable.$ref = this.$ref;
2223
return observable;
2324
}
2425

2526
push(val:any):firebase.database.ThenableReference {
26-
if(!this._ref) {
27+
if(!this.$ref) {
2728
throw new Error('No ref specified for this Observable!');
2829
}
29-
this._ref.ref
30-
return this._ref.ref.push(val);
30+
return this.$ref.ref.push(val);
3131
}
3232

3333
update(item: FirebaseOperation, value: Object): firebase.Promise<void> {
3434
return this._checkOperationCases(item, {
35-
stringCase: () => this._ref.ref.child(<string>item).update(value),
35+
stringCase: () => this.$ref.ref.child(<string>item).update(value),
3636
firebaseCase: () => (<firebase.database.Reference>item).update(value),
3737
snapshotCase: () => (<firebase.database.DataSnapshot>item).ref.update(value),
38-
unwrappedSnapshotCase: () => this._ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).update(value)
38+
unwrappedSnapshotCase: () => this.$ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).update(value)
3939
});
4040
}
4141

@@ -45,13 +45,13 @@ export class FirebaseListObservable<T> extends Observable<T> {
4545

4646
// if no item parameter is provided, remove the whole list
4747
if (!item) {
48-
return this._ref.ref.remove();
48+
return this.$ref.ref.remove();
4949
}
5050
return this._checkOperationCases(item, {
51-
stringCase: () => this._ref.ref.child(<string>item).remove(),
51+
stringCase: () => this.$ref.ref.child(<string>item).remove(),
5252
firebaseCase: () => (<firebase.database.Reference>item).remove(),
5353
snapshotCase: () => (<firebase.database.DataSnapshot>item).ref.remove(),
54-
unwrappedSnapshotCase: () => this._ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).remove()
54+
unwrappedSnapshotCase: () => this.$ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).remove()
5555
});
5656
}
5757

src/database/firebase_object_factory.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('FirebaseObjectFactory', () => {
7070

7171
it('should emit a null value if no value is present when subscribed', (done: any) => {
7272
subscription = observable.subscribe(unwrapped => {
73-
const expectedObject = { $key: (<any>observable)._ref.key, $value: null };
73+
const expectedObject = { $key: (<any>observable).$ref.key, $value: null };
7474
expect(unwrapped.$key).toEqual(expectedObject.$key);
7575
expect(unwrapped.$value).toEqual(expectedObject.$value);
7676
expect(unwrapped.$exists()).toEqual(false);

src/database/firebase_object_observable.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ describe('FirebaseObjectObservable', () => {
4747
expect(map.call(O, noop) instanceof FirebaseObjectObservable).toBe(true);
4848
});
4949

50+
describe('$ref', () => {
51+
// it('should be a firebase.database.Reference', () => {
52+
// expect(O.$ref instanceof database.Reference).toBe(true);
53+
// });
54+
55+
it('should match the database path passed in the constructor', () => {
56+
expect(O.$ref.toString()).toEqual(ref.toString());
57+
});
58+
});
59+
5060
describe('set', () => {
5161

5262
it('should call set on the underlying ref', (done:any) => {

src/database/firebase_object_observable.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,35 @@ import { Observable } from 'rxjs/Observable';
22
import { Operator } from 'rxjs/Operator';
33
import { Subscriber } from 'rxjs/Subscriber';
44
import { Subscription } from 'rxjs/Subscription';
5+
import * as firebase from 'firebase';
56

67
export class FirebaseObjectObservable<T> extends Observable<T> {
7-
constructor(subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void, private _ref?:firebase.database.Reference) {
8+
constructor(subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void, public $ref?:firebase.database.Reference) {
89
super(subscribe);
910
}
1011
lift<T, R>(operator: Operator<T, R>): Observable<R> {
1112
const observable = new FirebaseObjectObservable<R>();
1213
observable.source = this;
1314
observable.operator = operator;
14-
observable._ref = this._ref;
15+
observable.$ref = this.$ref;
1516
return observable;
1617
}
1718
set(value: any): firebase.Promise<void> {
18-
if(!this._ref) {
19+
if(!this.$ref) {
1920
throw new Error('No ref specified for this Observable!');
2021
}
21-
return this._ref.set(value);
22+
return this.$ref.set(value);
2223
}
2324
update(value: Object): firebase.Promise<void> {
24-
if(!this._ref) {
25+
if(!this.$ref) {
2526
throw new Error('No ref specified for this Observable!');
2627
}
27-
return this._ref.update(value);
28+
return this.$ref.update(value);
2829
}
2930
remove(): firebase.Promise<void> {
30-
if(!this._ref) {
31+
if(!this.$ref) {
3132
throw new Error('No ref specified for this Observable!');
3233
}
33-
return this._ref.remove();
34+
return this.$ref.remove();
3435
}
3536
}

0 commit comments

Comments
 (0)