Tutorial

Using Cloud Firestore in Angular With AngularFire

Published on October 4, 2017
Default avatar

By Alligator.io

Using Cloud Firestore in Angular With AngularFire

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

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

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:

app.module.tsc
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AngularFireModule } from 'angularfire2';
import { AngularFirestoreModule } from 'angularfire2/firestore';
import { environment } from '../environments/environment';
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:

/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';
import { AngularFirestore } from 'angularfire2/firestore';

@Component({ ... })
export class AppComponent {
  constructor(private afs: AngularFirestore) {
    // ...
  }
}

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:

app.component.ts
import { Component } from '@angular/core';
import {
  AngularFirestore,
  AngularFirestoreCollection
} from 'angularfire2/firestore';

import { Observable } from 'rxjs/Observable';
export interface Todo {
  description: string;
  completed: boolean;
}
@Component({ ... })
export class AppComponent {
  todoCollectionRef: AngularFirestoreCollection<Todo>;
  todo$: Observable<Todo[]>;

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

app.component.html
<ul>
  <li *ngFor="let todo of todo$ | async"
      [class.completed]="todo.completed">
    {{ todo.description }}
  </li>
</ul>

Adding items

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

addTodo(todoDesc: string) {
  if (todoDesc && 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:

app.component.ts
import { Component } from '@angular/core';
import {
  AngularFirestore,
  AngularFirestoreCollection
} from 'angularfire2/firestore';

import { Observable } from 'rxjs/Observable';
export interface Todo {
  id?: string;
  description: string;
  completed: boolean;
}
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todoCollectionRef: AngularFirestoreCollection<Todo>;
  todo$: Observable<Todo[]>;

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:

app.component.ts (partial)
updateTodo(todo: Todo) {
  this.todoCollectionRef.doc(todo.id).update({ completed: !todo.completed });
}

deleteTodo(todo: Todo) {
  this.todoCollectionRef.doc(todo.id).delete();
}

Learning more

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Alligator.io

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
2 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Could you please continue the implementation with the todo$ Observable in the CRUD? I can’t see a Read and Query methods implemented here. :)

You rock!! This post was helpful! Just one thing you might need to update, now use use @angular/fire instead of the deprecated ones. ;)

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel