|
1 | 1 | # Using Documents with Firestore
|
2 | 2 |
|
| 3 | +## Understanding Documents |
| 4 | + |
| 5 | +A Document is apart of a Collection. Firestore follows a `Collection > Document > Collection > Document` structure. Collections nested under a Document are not retrieved with a Document. You must explicitly retrieve the Collection underneath that path. This gives you a much more flexible structure. Gone are the days of worrying about pulling back the whole tree. |
| 6 | + |
| 7 | +## Using an AngularFirestoreDocument |
| 8 | + |
| 9 | +The `AngularFirestoreDocument` service is a wrapper around the native Firestore SDK's `DocumentReference` type. It is a generic service that provides you with a strongly typed set of methods for manipulating and streaming data. This service is designed for use as an `@Injectable()`. |
| 10 | + |
| 11 | +```ts |
| 12 | +import { Component } from '@angular/core'; |
| 13 | +import { AngularFirestore, AngularFirestoreDocument } from 'angularfire2/firestore'; |
| 14 | +import { Observable } from 'rxjs/Observable'; |
| 15 | + |
| 16 | +@Component({ |
| 17 | + selector: 'afs-app', |
| 18 | + template: ` |
| 19 | + <div> |
| 20 | + {{ (item | async)?.name }} |
| 21 | + </div> |
| 22 | + ` |
| 23 | +}) |
| 24 | +export class AppComponent { |
| 25 | + private itemDoc: AngularFirestoreDocument<Item>: |
| 26 | + item: Observable<Item>; |
| 27 | + constructor(private afs: AngularFirestore): { |
| 28 | + this.itemDoc = afs.doc<Item>('items/1'); |
| 29 | + this.item = this.itemDoc.valueChanges(); |
| 30 | + } |
| 31 | + update(item: Item) { |
| 32 | + this.itemDoc.update(item); |
| 33 | + } |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +### The `DocumentChangeAction` type |
| 38 | + |
| 39 | +With the exception of the `valueChanges()`, each streaming method returns an Observable of `DocumentChangeAction[]`. |
| 40 | + |
| 41 | +A `DocumentChangeAction` gives you the `type` and `payload` properties. The `type` tells when what `DocumentChangeType` operation occured (`added`, `modified`, `removed`). The `payload` property is a `DocumentChange` which provides you important metdata about the change and a `doc` property which is the `DocumentSnapshot`. |
| 42 | + |
| 43 | +```ts |
| 44 | +interface DocumentChangeAction { |
| 45 | + //'added' | 'modified' | 'removed'; |
| 46 | + type: DocumentChangeType; |
| 47 | + payload: DocumentChange; |
| 48 | +} |
| 49 | + |
| 50 | +interface DocumentChange { |
| 51 | + type: DocumentChangeType; |
| 52 | + doc: DocumentSnapshot; |
| 53 | + oldIndex: number; |
| 54 | + newIndex: number; |
| 55 | +} |
| 56 | + |
| 57 | +interface DocumentSnapshot { |
| 58 | + exists: boolean; |
| 59 | + ref: DocumentReference; |
| 60 | + id: string; |
| 61 | + metadata: SnapshotMetadata; |
| 62 | + data(): DocumentData; |
| 63 | + get(fieldPath: string): any; |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +## Streaming document data |
| 68 | + |
| 69 | +There are multiple ways of streaming collection data from Firestore. |
| 70 | + |
| 71 | +### `valueChanges()` |
| 72 | +**What is it?** - Returns an Observable of document data. All Snapshot metadata is stripped and just the method provides only the data. |
| 73 | + |
| 74 | +**Why would you use it?** - When you just need the object data. No document metadata is attached which makes it simple to render to a view. |
| 75 | + |
| 76 | +**When would you not use it?** - When you need the `id` of the document to use data manipulation metods. This method assumes you either are saving the `id` to the document data or using a "readonly" approach. |
| 77 | + |
| 78 | +### `snapshotChanges()` |
| 79 | +**What is it?** - Returns an Observable of data as a `DocumentChangeAction`. |
| 80 | + |
| 81 | +**Why would you use it?** - When you need the document data but also want to keep around metadata. This metadata provides you the underyling `DocumentReference` and document id. 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. |
| 82 | + |
| 83 | +**When would you not use it?** - When you simply need to render data to a view and don't want to do any extra processing. |
| 84 | + |
| 85 | +## Manipulating documents |
| 86 | + |
| 87 | +AngularFirestore provides methods for setting, updating, and deleting document data. |
| 88 | + |
| 89 | +- `set(data: T)` - Destructively updates a document's data. |
| 90 | +- `update(data: T)` - Non-destructively updates a document's data. |
| 91 | +- `delete()` - Deletes an entire document. Does not delete any nested collections. |
| 92 | + |
| 93 | +## Querying? |
| 94 | + |
| 95 | +Querying has no effect on documents. Documents are a single object and querying effects a range of multiple documents. If you are looking for querying then you want to use a collection. |
| 96 | + |
| 97 | +## Retrieving nested collections |
| 98 | + |
| 99 | +Nesting collections is a great way to structure your data. This allows you to group related data structures together. If you are creating a "Task List" site, you can group "tasks" under a user: `user/<uid>/tasks`. |
| 100 | + |
| 101 | +To retrieve a nested collection use the `collection(path: string)` method. |
| 102 | + |
| 103 | +```ts |
| 104 | + constructor(private afs: AngularFirestore): { |
| 105 | + this.userDoc = afs.doc<Item>('user/david'); |
| 106 | + this.tasks = this.userDoc.collection<Task>('tasks').valueChanges(); |
| 107 | + } |
| 108 | +``` |
| 109 | + |
| 110 | +For more information about using collections read the collection documentation. |
0 commit comments