Skip to content

Commit db73285

Browse files
committed
Merged master
2 parents ec1dfbf + 7108875 commit db73285

File tree

10 files changed

+225
-167
lines changed

10 files changed

+225
-167
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: node_js
22
sudo: false
33
node_js:
4-
- 'node'
4+
- 'lts/*'
55

66
addons:
77
chrome: stable

docs/firestore/collections.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ interface DocumentSnapshot {
7373

7474
There are multiple ways of streaming collection data from Firestore.
7575

76-
### `valueChanges()`
76+
### `valueChanges({idField?: string})`
7777

78-
**What is it?** - The current state of your collection. Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the method provides only the data.
78+
**What is it?** - The current state of your collection. Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the document data is included. Optionally, you can pass an options object with an `idField` key containing a string. If provided, the returned JSON objects will include their document ID mapped to a property with the name provided by `idField`.
7979

8080
**Why would you use it?** - When you just need a list of data. No document metadata is attached to the resulting array which makes it simple to render to a view.
8181

82-
**When would you not use it?** - When you need a more complex data structure than an array or you need the `id` of each document to use data manipulation methods. This method assumes you either are saving the `id` to the document data or using a "readonly" approach.
82+
**When would you not use it?** - When you need a more complex data structure than an array.
8383

8484
**Best practices** - Use this method to display data on a page. It's simple but effective. Use `.snapshotChanges()` once your needs become more complex.
8585

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@angular/core": ">=6.0.0 <8",
3636
"@angular/platform-browser": ">=6.0.0 <8",
3737
"@angular/platform-browser-dynamic": ">=6.0.0 <8",
38-
"firebase": ">=5.5.0 <7",
38+
"firebase": ">= 5.5.0 <7",
3939
"rxjs": "^6.0.0",
4040
"ws": "^3.3.2",
4141
"xhr2": "^0.1.4",
@@ -46,9 +46,9 @@
4646
"utf-8-validate": "~4.0.0"
4747
},
4848
"devDependencies": {
49+
"@angular/animations": ">=6.0.0 <8",
4950
"@angular/compiler-cli": ">=6.0.0 <8",
5051
"@angular/platform-server": ">=6.0.0 <8",
51-
"@angular/animations": ">=6.0.0 <8",
5252
"@types/jasmine": "^2.5.36",
5353
"@types/request": "0.0.30",
5454
"concurrently": "^2.2.0",

src/core/firebase.app.module.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { InjectionToken, NgModule, Optional } from '@angular/core';
2-
import { app, auth, database, firestore, functions, messaging, storage } from 'firebase/app';
3-
2+
import { auth, database, firestore, functions, messaging, storage } from 'firebase/app';
43
// @ts-ignore (https://github.com/firebase/firebase-js-sdk/pull/1206)
54
import firebase from 'firebase/app'; // once fixed can pull in as "default as firebase" above
65

@@ -20,14 +19,11 @@ export type FirebaseFunctions = functions.Functions;
2019

2120
// Have to implement as we need to return a class from the provider, we should consider exporting
2221
// this in the firebase/app types as this is our highest risk of breaks
23-
export class FirebaseApp implements app.App {
22+
export class FirebaseApp {
2423
name: string;
2524
options: {};
2625
auth: () => FirebaseAuth;
27-
// app.App database() doesn't take a databaseURL arg in the public types?
2826
database: (databaseURL?: string) => FirebaseDatabase;
29-
// automaticDataCollectionEnabled is now private? _automaticDataCollectionEnabled?
30-
// automaticDataCollectionEnabled: true,
3127
messaging: () => FirebaseMessaging;
3228
performance: () => any;
3329
storage: (storageBucket?: string) => FirebaseStorage;

src/firestore/collection/collection.spec.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { FirebaseApp, AngularFireModule } from '@angular/fire';
22
import { AngularFirestore } from '../firestore';
33
import { AngularFirestoreModule } from '../firestore.module';
4-
import { AngularFirestoreDocument } from '../document/document';
54
import { AngularFirestoreCollection } from './collection';
65
import { QueryFn } from '../interfaces';
76
import { Observable, BehaviorSubject, Subscription } from 'rxjs';
@@ -30,7 +29,7 @@ describe('AngularFirestoreCollection', () => {
3029
TestBed.configureTestingModule({
3130
imports: [
3231
AngularFireModule.initializeApp(COMMON_CONFIG),
33-
AngularFirestoreModule.enablePersistence()
32+
AngularFirestoreModule.enablePersistence({synchronizeTabs: true})
3433
]
3534
});
3635
inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
@@ -39,8 +38,8 @@ describe('AngularFirestoreCollection', () => {
3938
})();
4039
});
4140

42-
afterEach(async (done) => {
43-
await app.delete();
41+
afterEach(done => {
42+
app.delete();
4443
done();
4544
});
4645

@@ -70,6 +69,19 @@ describe('AngularFirestoreCollection', () => {
7069

7170
});
7271

72+
it('should optionally map the doc ID to the emitted data object', async (done: any) => {
73+
const ITEMS = 1;
74+
const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
75+
const idField = 'myCustomID';
76+
const sub = stocks.valueChanges({idField}).subscribe(data => {
77+
sub.unsubscribe();
78+
const stock = data[0];
79+
expect(stock[idField]).toBeDefined();
80+
expect(stock).toEqual(jasmine.objectContaining(FAKE_STOCK_DATA));
81+
deleteThemAll(names, ref).then(done).catch(fail);
82+
})
83+
});
84+
7385
it('should handle multiple subscriptions (hot)', async (done: any) => {
7486
const ITEMS = 4;
7587
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);

src/firestore/collection/collection.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,29 @@ export class AngularFirestoreCollection<T=DocumentData> {
103103

104104
/**
105105
* Listen to all documents in the collection and its possible query as an Observable.
106+
*
107+
* If the `idField` option is provided, document IDs are included and mapped to the
108+
* provided `idField` property name.
109+
* @param options
106110
*/
107-
valueChanges(): Observable<T[]> {
111+
valueChanges(): Observable<T[]>
112+
valueChanges({}): Observable<T[]>
113+
valueChanges<K extends string>(options: {idField: K}): Observable<(T & { [T in K]: string })[]>
114+
valueChanges<K extends string>(options: {idField?: K} = {}): Observable<T[]> {
108115
const fromCollectionRef$ = fromCollectionRef<T>(this.query);
109116
const scheduled$ = this.afs.scheduler.runOutsideAngular(fromCollectionRef$);
110117
return this.afs.scheduler.keepUnstableUntilFirst(scheduled$)
111118
.pipe(
112-
map(actions => actions.payload.docs.map(a => a.data()))
119+
map(actions => actions.payload.docs.map(a => {
120+
if (options.idField) {
121+
return {
122+
...a.data() as Object,
123+
...{ [options.idField]: a.id }
124+
} as T & { [T in K]: string };
125+
} else {
126+
return a.data()
127+
}
128+
}))
113129
);
114130
}
115131

src/firestore/document/document.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('AngularFirestoreDocument', () => {
1919
TestBed.configureTestingModule({
2020
imports: [
2121
AngularFireModule.initializeApp(COMMON_CONFIG),
22-
AngularFirestoreModule.enablePersistence()
22+
AngularFirestoreModule.enablePersistence({synchronizeTabs: true})
2323
]
2424
});
2525
inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
@@ -28,8 +28,8 @@ describe('AngularFirestoreDocument', () => {
2828
})();
2929
});
3030

31-
afterEach(async (done) => {
32-
await app.delete();
31+
afterEach(done => {
32+
app.delete();
3333
done();
3434
});
3535

src/firestore/firestore.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('AngularFirestore', () => {
2323
TestBed.configureTestingModule({
2424
imports: [
2525
AngularFireModule.initializeApp(COMMON_CONFIG),
26-
AngularFirestoreModule.enablePersistence({experimentalTabSynchronization: true})
26+
AngularFirestoreModule.enablePersistence({synchronizeTabs: true})
2727
]
2828
});
2929
inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
@@ -116,7 +116,8 @@ describe('AngularFirestore with different app', () => {
116116
});
117117

118118
afterEach(done => {
119-
app.delete().then(done, done.fail);
119+
app.delete();
120+
done();
120121
});
121122

122123
describe('<constructor>', () => {
@@ -155,6 +156,11 @@ describe('AngularFirestore without persistance', () => {
155156
})();
156157
});
157158

159+
afterEach(done => {
160+
app.delete();
161+
done();
162+
});
163+
158164
it('should not enable persistence', (done) => {
159165
afs.persistenceEnabled$.subscribe(isEnabled => {
160166
expect(isEnabled).toBe(false);

src/storage/ref.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface AngularFireStorageReference {
99
delete(): Observable<any>;
1010
child(path: string): any;
1111
updateMetatdata(meta: SettableMetadata): Observable<any>;
12+
updateMetadata(meta: SettableMetadata): Observable<any>;
1213
put(data: any, metadata?: UploadMetadata | undefined): AngularFireUploadTask;
1314
putString(data: string, format?: string | undefined, metadata?: UploadMetadata | undefined): AngularFireUploadTask;
1415
}
@@ -33,6 +34,7 @@ export function createStorageRef(ref: Reference, scheduler: FirebaseZoneSchedule
3334
delete: () => from(ref.delete()),
3435
child: (path: string) => createStorageRef(ref.child(path), scheduler),
3536
updateMetatdata: (meta: SettableMetadata) => from(ref.updateMetadata(meta)),
37+
updateMetadata: (meta: SettableMetadata) => from(ref.updateMetadata(meta)),
3638
put: (data: any, metadata?: UploadMetadata) => {
3739
const task = ref.put(data, metadata);
3840
return createUploadTask(task);

0 commit comments

Comments
 (0)