Skip to content

Commit 21442f0

Browse files
committed
fix(afs): allow stateChanges and auditLog to emit blank arrays at first
This allows a developer to detirmine that the collection being observed is empty and keeps the subscription from holding up isStable.
1 parent 829df50 commit 21442f0

File tree

5 files changed

+37
-17
lines changed

5 files changed

+37
-17
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular/fire",
3-
"version": "6.1.2",
3+
"version": "6.1.3",
44
"description": "The official Angular library for Firebase.",
55
"private": true,
66
"scripts": {

src/auth/auth.spec.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,21 @@ describe('AngularFireAuth', () => {
118118

119119
});
120120

121-
const FIREBASE_APP_NAME_TOO = (Math.random() + 1).toString(36).substring(7);
122-
123121
describe('AngularFireAuth with different app', () => {
124122
let app: FirebaseApp;
125123
let afAuth: AngularFireAuth;
124+
let firebaseAppName: string;
126125

127126
beforeEach(() => {
127+
firebaseAppName = rando();
128+
128129
TestBed.configureTestingModule({
129130
imports: [
130131
AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
131132
AngularFireAuthModule
132133
],
133134
providers: [
134-
{ provide: FIREBASE_APP_NAME, useValue: FIREBASE_APP_NAME_TOO },
135+
{ provide: FIREBASE_APP_NAME, useValue: firebaseAppName },
135136
{ provide: FIREBASE_OPTIONS, useValue: COMMON_CONFIG }
136137
]
137138
});
@@ -156,7 +157,7 @@ describe('AngularFireAuth with different app', () => {
156157

157158
it('should have an initialized Firebase app instance member', async () => {
158159
const app = await afAuth.app;
159-
expect(app.name).toEqual(FIREBASE_APP_NAME_TOO);
160+
expect(app.name).toEqual(firebaseAppName);
160161
});
161162
});
162163

src/firestore/collection/collection.spec.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ describe('AngularFirestoreCollection', () => {
399399
const ITEMS = 10;
400400
const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
401401

402-
const sub = stocks.stateChanges(['modified']).subscribe(data => {
402+
const sub = stocks.stateChanges(['modified']).pipe(skip(1), take(1)).subscribe(data => {
403403
sub.unsubscribe();
404404
expect(data.length).toEqual(1);
405405
expect(data[0].payload.doc.data().price).toEqual(2);
@@ -436,7 +436,7 @@ describe('AngularFirestoreCollection', () => {
436436
const ITEMS = 10;
437437
const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
438438

439-
const sub = stocks.stateChanges(['removed']).subscribe(data => {
439+
const sub = stocks.stateChanges(['removed']).pipe(skip(1), take(1)).subscribe(data => {
440440
sub.unsubscribe();
441441
expect(data.length).toEqual(1);
442442
expect(data[0].type).toEqual('removed');
@@ -446,6 +446,21 @@ describe('AngularFirestoreCollection', () => {
446446

447447
delayDelete(stocks, names[0], 400);
448448
});
449+
450+
it('stateChanges() should emit on empty collection', async (done) => {
451+
afs.collection('EMPTY_COLLECTION').stateChanges().pipe(take(1)).subscribe(data => {
452+
expect(data.length).toEqual(0);
453+
done();
454+
});
455+
});
456+
457+
it('stateChanges() w/filter should emit on empty collection', async (done) => {
458+
afs.collection('EMPTY_COLLECTION').stateChanges(['added']).pipe(take(1)).subscribe(data => {
459+
expect(data.length).toEqual(0);
460+
done();
461+
});
462+
});
463+
449464
});
450465

451466
describe('auditTrail()', () => {
@@ -471,7 +486,7 @@ describe('AngularFirestoreCollection', () => {
471486
const ITEMS = 10;
472487
const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
473488

474-
const sub = stocks.auditTrail(['removed']).subscribe(data => {
489+
const sub = stocks.auditTrail(['removed']).pipe(skip(1), take(1)).subscribe(data => {
475490
sub.unsubscribe();
476491
expect(data.length).toEqual(1);
477492
expect(data[0].type).toEqual('removed');

src/firestore/collection/collection.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { from, Observable } from 'rxjs';
22
import { fromCollectionRef } from '../observable/fromRef';
3-
import { filter, map, observeOn, scan } from 'rxjs/operators';
3+
import { filter, map, observeOn, pairwise, scan, startWith } from 'rxjs/operators';
44
import firebase from 'firebase/app';
55

66
import { CollectionReference, DocumentChangeAction, DocumentChangeType, DocumentData, DocumentReference, Query } from '../interfaces';
@@ -59,15 +59,19 @@ export class AngularFirestoreCollection<T = DocumentData> {
5959
* your own data structure.
6060
*/
6161
stateChanges(events?: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
62-
if (!events || events.length === 0) {
63-
return docChanges<T>(this.query, this.afs.schedulers.outsideAngular).pipe(
64-
filter(changes => changes.length > 0),
65-
this.afs.keepUnstableUntilFirst
62+
let source = docChanges<T>(this.query, this.afs.schedulers.outsideAngular);
63+
if (events && events.length > 0) {
64+
source = source.pipe(
65+
map(actions => actions.filter(change => events.indexOf(change.type) > -1))
6666
);
6767
}
68-
return docChanges<T>(this.query, this.afs.schedulers.outsideAngular).pipe(
69-
map(actions => actions.filter(change => events.indexOf(change.type) > -1)),
70-
filter(changes => changes.length > 0),
68+
return source.pipe(
69+
// We want to filter out empty arrays, but always emit at first, so the developer knows
70+
// that the collection has been resolve; even if it's empty
71+
startWith(undefined),
72+
pairwise(),
73+
filter(([prior, current]) => current.length > 0 || !prior),
74+
map(([prior, current]) => current),
7175
this.afs.keepUnstableUntilFirst
7276
);
7377
}

src/firestore/utils.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@ export function delayDelete<T>(collection: AngularFirestoreCollection<T>|firebas
5454
}, delay);
5555
}
5656

57-
export const rando = () => (Math.random() + 1).toString(36).substring(7);
57+
export const rando = () => (Math.random() + 1).toString(36).split('.')[1];

0 commit comments

Comments
 (0)