Skip to content

Commit 7108875

Browse files
codediodeiojamesdaniels
authored andcommitted
feat(firestore): Added option to include document IDs on valueChanges() (#1976)
An 'idField' option can be used with collection.valueChanges() to include the document ID on the emitted data payload.
1 parent 6133296 commit 7108875

File tree

3 files changed

+34
-6
lines changed

3 files changed

+34
-6
lines changed

docs/firestore/collections.md

+3-3
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

src/firestore/collection/collection.spec.ts

+13-1
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';
@@ -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

+18-2
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

0 commit comments

Comments
 (0)