Skip to content

Commit 8185ca2

Browse files
authored
docs(afs): Add collection method examples (#5)
1 parent 60d153b commit 8185ca2

File tree

2 files changed

+165
-6
lines changed

2 files changed

+165
-6
lines changed

docs/firestore/collections.md

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { Component } from '@angular/core';
1212
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
1313
import { Observable } from 'rxjs/Observable';
1414

15+
export interface Item { name: string; }
16+
1517
@Component({
1618
selector: 'app-root',
1719
template: `
@@ -69,28 +71,137 @@ interface DocumentSnapshot {
6971

7072
## Streaming collection data
7173

72-
There are multiple ways of streaming collection data from Firestore.
74+
There are multiple ways of streaming collection data from Firestore.
7375

7476
### `valueChanges()`
75-
**What is it?** - 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.
77+
**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.
7678

7779
**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.
7880

7981
**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 metods. This method assumes you either are saving the `id` to the document data or using a "readonly" approach.
8082

83+
**Best practices** - Use this method to display data on a page. It's simple but effective. Use `.snapshotChanges()` once your needs become more complex.
84+
85+
#### Example of persisting a Document Id
86+
```ts
87+
import { Component } from '@angular/core';
88+
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
89+
import { Observable } from 'rxjs/Observable';
90+
91+
export interface Item { id: string; name: string; }
92+
93+
@Component({
94+
selector: 'app-root',
95+
template: `
96+
<ul>
97+
<li *ngFor="let item of items | async">
98+
{{ item.name }}
99+
</li>
100+
</ul>
101+
`
102+
})
103+
export class AppComponent {
104+
private itemsCollection: AngularFirestoreCollection<Item>;
105+
items: Observable<Item[]>;
106+
constructor(private readonly afs: AngularFirestore) {
107+
this.itemsCollection = afs.collection<Item>('items');
108+
// .valueChanges() is simple. It just returns the
109+
// JSON data without metadata. If you need the
110+
// doc.id() in the value you must persist it your self
111+
// or use .snapshotChanges() instead. See the addItem()
112+
// method below for how to persist the id with
113+
// valueChanges()
114+
this.items = this.itemsCollection.valueChanges();
115+
}
116+
addItem(name: string) {
117+
// Persist a document id
118+
const id = this.afs.createId();
119+
const item: Item = { id, item };
120+
this.itemsCollection.add(item);
121+
}
122+
}
123+
```
124+
81125
### `snapshotChanges()`
82-
**What is it?** - Returns an Observable of data as a synchronized array of `DocumentChangeAction[]`.
126+
**What is it?** - The current state of your collection. Returns an Observable of data as a synchronized array of `DocumentChangeAction[]`.
83127

84128
**Why would you use it?** - When you need a list of data but also want to keep around metadata. Metadata provides you the underyling `DocumentReference`, document id, and array index of the single document. Having the document's id around makes it easier to use data manipulation methods. This method gives you more horsepower with other Angular integrations such as ngrx, forms, and animations due to the `type` property. The `type` property on each `DocumentChangeAction` is useful for ngrx reducers, form states, and animation states.
85129

86130
**When would you not use it?** - When you need a more complex data structure than an array or if you need to process changes as they occur. This array is synchronized with the remote and local changes in Firestore.
87131

132+
**Best practices** - Use an observable operator to transform your data from `.snapshotChanges()`. Don't return the `DocumentChangeAction[]` to the template. See the example below.
133+
134+
#### Example
135+
```ts
136+
import { Component } from '@angular/core';
137+
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
138+
import { Observable } from 'rxjs/Observable';
139+
import 'rxjs/add/operator/map';
140+
141+
export interface Shirt { name: string; price: number; }
142+
143+
@Component({
144+
selector: 'app-root',
145+
template: `
146+
<ul>
147+
<li *ngFor="let shirt of shirts | async">
148+
{{ shirt.name }} is {{ shirt.price }}
149+
</li>
150+
</ul>
151+
`
152+
})
153+
export class AppComponent {
154+
private shirtCollection: AngularFirestoreCollection<Shirt>;
155+
shirts: Observable<Shirt[]>;
156+
constructor(private readonly afs: AngularFirestore) {
157+
this.shirtCollection = afs.collection<Shirt>('shirts');
158+
// .snapshotChanges() returns a DocumentChangeAction[], which contains
159+
// a lot of information about "what happened" with each change. If you want to
160+
// get the data and the id use the map operator.
161+
this.shirts = this.shirtCollection.snapshotChanges().map(actions => {
162+
return actions.map(a => ({ id: a.payload.doc.id(), ...a.payload.doc.data() }))
163+
});
164+
}
165+
}
166+
```
167+
88168
### `stateChanges()`
89169
**What is it?** - Returns an Observable of the most recent changes as a `DocumentChangeAction[]`.
90170

91171
**Why would you use it?** - The above methods return a synchronized array sorted in query order. `stateChanges()` emits changes as they occur rather than syncing the query order. This works well for ngrx integrations as you can build your own data structure in your reducer methods.
92172

93-
**When would you not use it?** - When you just need a list of data. This is a more advanced usage of AngularFirestore.
173+
**When would you not use it?** - When you just need a list of data. This is a more advanced usage of AngularFirestore.
174+
175+
#### Example
176+
```ts
177+
import { Component } from '@angular/core';
178+
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
179+
import { Observable } from 'rxjs/Observable';
180+
181+
export interface AccountDeposit { description: string; amount: number; }
182+
183+
@Component({
184+
selector: 'app-root',
185+
template: `
186+
<ul>
187+
<li *ngFor="let deposit of deposits | async">
188+
{{ deposit.description }} for {{ deposit.amount }}
189+
</li>
190+
</ul>
191+
`
192+
})
193+
export class AppComponent {
194+
private depositCollection: AngularFirestoreCollection<Shirt>;
195+
deposits: Observable<Shirt[]>;
196+
constructor(private readonly afs: AngularFirestore) {
197+
this.depositCollection = afs.collection<AccountDeposit>('deposits');
198+
this.deposits = this.shirtCollection.stateChanges(['added'])
199+
.map(actions => {
200+
return actions.map(a => ({ id: a.payload.doc.id(), ...a.payload.doc.data() }))
201+
});
202+
}
203+
}
204+
```
94205

95206
### `auditTrail()`
96207
**What is it?** - Returns an Observable of `DocumentChangeAction[]` as they occur. Similar to `stateChanges()`, but instead it keeps around the trail of events as an array.
@@ -99,9 +210,42 @@ There are multiple ways of streaming collection data from Firestore.
99210

100211
**When would you not use it?** - When you just need a list of data. This is a more advanced usage of AngularFirestore.
101212

213+
**Best Practices** -
214+
215+
#### Example
216+
```ts
217+
import { Component } from '@angular/core';
218+
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
219+
import { Observable } from 'rxjs/Observable';
220+
221+
export interface AccountLogItem { description: string; amount: number; }
222+
223+
@Component({
224+
selector: 'app-root',
225+
template: `
226+
<ul>
227+
<li *ngFor="let log of accountLogs | async">
228+
{{ log.description }} for {{ log.amount }}
229+
</li>
230+
</ul>
231+
`
232+
})
233+
export class AppComponent {
234+
private accountLogCollection: AngularFirestoreCollection<AccountLogItem>;
235+
accountLogs: Observable<AccountLogItem[]>;
236+
constructor(private readonly afs: AngularFirestore) {
237+
this.accountLogCollection = afs.collection<AccountDeposit>('accountLog');
238+
this.accountLogs = this.shirtCollection.auditTrail()
239+
.map(actions => {
240+
return actions.map(a => ({ id: a.payload.doc.id(), ...a.payload.doc.data() }))
241+
});
242+
}
243+
}
244+
```
245+
102246
### Limiting events
103247

104-
There are three `DocumentChangeType`s in Firestore: `added`, `removed`, and `moved`. Each streaming method listens to all three by default. However, your site may only be intrested in one of these events. You can specify which events you'd like to use through the first parameter of each method:
248+
There are three `DocumentChangeType`s in Firestore: `added`, `removed`, and `modified`. Each streaming method listens to all three by default. However, you may only be intrested in one of these events. You can specify which events you'd like to use through the first parameter of each method:
105249

106250
#### Basic smaple
107251
```ts
@@ -111,6 +255,8 @@ There are three `DocumentChangeType`s in Firestore: `added`, `removed`, and `mov
111255
}
112256
```
113257

258+
**Note:** Using
259+
114260
#### Component Sample
115261
```ts
116262
import { Component } from '@angular/core';
@@ -132,11 +278,17 @@ export class AppComponent {
132278
items: Observable<Item[]>;
133279
constructor(private afs: AngularFirestore) {
134280
this.itemsCollection = afs.collection<Item>('items');
135-
this.items = this.itemsCollection.valueChanges();
281+
this.items = this.itemsCollection.valueChanges(['added', 'removed']);
136282
}
137283
}
138284
```
139285

286+
## State based vs. action based
287+
288+
Each one of these methods falls into two categories: state based and action based. State based methods return the state of your collection "as-is". Whereas action based methods return "what happened" in your collection.
289+
290+
For example, a user updates the third item in a list. In a state based method like `.valueChanges()` will update the third item in the collection and return an array of JSON data. This is how your state looks.
291+
140292
## Adding documents to a collection
141293

142294
To add a new document to a collection with a generated id use the `add()` method. This method uses the type provided by the generic class to validate it's type structure.

src/firestore/firestore.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,11 @@ export class AngularFirestore {
127127
const ref = this.firestore.doc(path);
128128
return new AngularFirestoreDocument<T>(ref);
129129
}
130+
131+
/**
132+
* Returns a generated Firestore Document Id.
133+
*/
134+
createId() {
135+
return this.firestore.collection('_').doc().id
136+
}
130137
}

0 commit comments

Comments
 (0)