Cloud Firestore was just announced as a new database to fill-in the gap where the Firebase realtime database may not be the best tool. Cloud Firestore is a NoSQL, document-based database. At the root of the database are collections (e.g.: <^>todos<^>, <^>users<^>, <^>files<^>) and collections can only contain documents. Documents contain key-value pairs and can contain collections of their own, called <^>subcollections<^>. This therefore makes it easier to build apps that have more complex hierarchical needs compared with the flat JSON tree offered with the traditional Firebase realtime database.

Here's we'll cover the very basics of using interacting with Cloud Firestore in an Angular 2+ project. You'll need to have a Firebase account and to enable the new database.

Setup

angular illustration for: Setup

First, install the needed Firebase packages (<^>firebase<^> & <^>angularfire2<^>) into your Angular project:

				
					
$ yarn add firebase angularfire2



# or, using npm:

$ npm install firebase angularfire2

				
			

And then add both the <^>AngularFireModule<^> and <^>AngularFirestoreModule<^> to your app module:

				
					
[label app.module.tsc]

import { BrowserModule } from '@angular/platform-browser';

import { NgModule } from '@angular/core';



&lt;^&gt;import { AngularFireModule } from 'angularfire2';&lt;^&gt;

&lt;^&gt;import { AngularFirestoreModule } from 'angularfire2/firestore';&lt;^&gt;

&lt;^&gt;import { environment } from '../environments/environment';&lt;^&gt;

import { AppComponent } from './app.component';



				
			

Note that we call the <^>enablePersistence<^> method on the AngularFirestoreModule to automatically enable local caching on Chrome, Safari and Firefox, which will allow the app to stay available while offline.

And with this, your Firebase app configuration would be inside the enviroment.ts file like this:

				
					
[label /enviroments/enviroment.ts]

export const environment = {

 production: false,

 firebase: {

 apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',

 authDomain: 'your-project-id.firebaseapp.com',

 databaseURL: 'https://your-project-id.firebaseio.com',

 projectId: 'your-project-id',

 storageBucket: 'your-project-id.appspot.com',

 messagingSenderId: 'XXXXXXXXXXXX'

 }

};

				
			

Basic Usage

Now that our app is configured with Firebase, we can start playing around with the database. We'll demonstrate a few of the CRUD operations around the idea of a simple todo app that contains a <^>todos<^> collection and documents within that collection that contain a <^>description<^> and a <^>completed<^> field.

First you'll want to inject the <^>AngularFirestore<^> injectable into your component:

				
					
import { Component } from '@angular/core';

&lt;^&gt;import { AngularFirestore } from 'angularfire2/firestore';&lt;^&gt;



@Component({ ... })

export class AppComponent {

 constructor(&lt;^&gt;private afs: AngularFirestore&lt;^&gt;) {

 // ...

 }

}

				
			

To get access to a collection, you create a reference to it with something like this:

				
					
this.todoCollectionRef = this.afs.collection('todos'); // a ref to the todos collection

				
			

Creating a reference to a collection doesn't do any network call and it's safe to reference collections that don't exist, as the collection will be automatically created if needed. A collection without any document will also be automatically removed.

You can then listen for changes on the collection's documents by calling <^>valueChanges()<^> on the collection reference:

				
					
this.todo$ = this.todoCollectionRef.valueChanges();

				
			

The valueChanges method returns an observable of the documents in the collection. There's also a <^>snapshotChanges<^>, method that also returns the id as well as metadata about our documents. Read-on for an example using snapshotChanges instead.

Here's therefore our starting todo app, which will get the documents inside the <^>todos<^> collection from the database:

				
					
[label app.component.ts]

import { Component } from '@angular/core';

import {

 AngularFirestore,

 &lt;^&gt;AngularFirestoreCollection&lt;^&gt;

} from 'angularfire2/firestore';



import { Observable } from 'rxjs/Observable';

export interface Todo {

 description: string;

 completed: boolean;

}

@Component({ ... })

export class AppComponent {

 &lt;^&gt;todoCollectionRef: AngularFirestoreCollection&lt;Todo&gt;;&lt;^&gt;

 &lt;^&gt;todo$: Observable&lt;Todo[]&gt;;&lt;^&gt;



				
			

And we can display our todo items in the template with something as simple as this:

				
					
[label app.component.html]

&lt;ul&gt;

 &lt;li *ngFor="let todo of todo$ | async"

 [class.completed]="todo.completed"&gt;

 {{ todo.description }}

 &lt;/li&gt;

&lt;/ul&gt;

				
			

Adding items

To add a new document in a collection, simply call <^>add<^> on the collection reference:

				
					
addTodo(todoDesc: string) {

 if (todoDesc &amp;&amp; todoDesc.trim().length) {

 this.todoCollectionRef.add({ description: todoDesc, completed: false });

 }

}

				
			

Thanks to our todo collection reference being typed against our <^>Todo<^> interface, the TypeScript compiler will know the shape of the data that should be passed using the add method.

Updating and Deleting Items

To update or delete a document in the collection, we'll also need the document's <^>id<^>, which is not returned with the <^>valueChanges<^> method. We'll change our implementation a bit to use the <^>snapshotChanges<^> method instead and also include the id for each todo document:

				
					
[label app.component.ts]

import { Component } from '@angular/core';

import {

 AngularFirestore,

 AngularFirestoreCollection

} from 'angularfire2/firestore';



import { Observable } from 'rxjs/Observable';

export interface Todo {

 &lt;^&gt;id?: string;&lt;^&gt;

 description: string;

 completed: boolean;

}

@Component({

 selector: 'app-root',

 templateUrl: './app.component.html',

 styleUrls: ['./app.component.css']

})

export class AppComponent {

 todoCollectionRef: AngularFirestoreCollection&lt;Todo&gt;;

 todo$: Observable&lt;Todo[]&gt;;



				
			

We map over the observable returned by <^>snapshotChanges<^> to extract the document's id along with its data.

Actions returned by <^>snapshotChanges<^> are of type <^>DocumentChangeAction<^> and contain a type and a payload. The type is either <^>added<^>, <^>modified<^> or <^>removed<^> and the payload contains the document's id, metadata and data.

With this in place, everything still works the same, and we can now easily implement updating and deleting todo documents and have the changes reflected immediately to our template:

				
					
[label app.component.ts (partial)]

updateTodo(todo: Todo) {

 &lt;^&gt;this.todoCollectionRef.doc(todo.id).update({ completed: !todo.completed });&lt;^&gt;

}



deleteTodo(todo: Todo) {

 &lt;^&gt;this.todoCollectionRef.doc(todo.id).delete();&lt;^&gt;

}

				
			

Learning more

  • See this post of ours to learn about using the Firebase realtime database in Angular.