Starting With Google Cloud Firestore Database With Angular

What is cloud Firestore?

Cloud Firestore is a NoSQL Document database built for automatic scaling, high performance, and ease of application development. Unlike an SQL database, there are no tables or rows. Instead, you store the data in documents organized into a collection.
cloud Firestore

All documents must be stored in collections. Documents can contain subcollections and nested objects, both of which can include primitive fields (like strings) or complex objects (like lists).

Collections and documents are created implicitly in Cloud Firestore. Simply, assign the data to a document within a collection. If either the collection or document does not exist, Cloud Firestore creates it.

Document

In Cloud Firestore, the unit of storage is the document. A document is a lightweight record that contains fields which map to values.

Collections

Documents live in collections, which are simply containers for documents.

cloud Firestore

Here, users_data is the collection and document name will be obtained by auto key value.

(Source Credit Google Cloud Platform)

So now, it is time for some action. We will see how to create a Firestore document and how to insert/fetch the data from the Firestore database.

Prerequisites

First, execute the following command.

npm install firebase angularfire2 –save

Cloud Firestore Setup

After logging in to Firebase console account,  follow the below steps.

  • Create Project.
  • Navigate to Database tab.

    cloud Firestore

  • Select Cloud Firestore Database and create a project in test mode.

    cloud Firestore

  • Now, add a collection by clicking Add Collection.

    cloud Firestore

    cloud Firestore
    cloud Firestore

Setting up Angular Application

  • First, get the configuration to connect Firestore from Angular by clicking the "Setting" button.

    cloud Firestore

  • Now, store all the configuration information.

    cloud Firestore
Environments.ts
  1. export const environment = {  
  2.   production: false,  
  3.   firebase: {  
  4.     apiKey: 'your key',  
  5.     authDomain: 'your auth domain',  
  6.     databaseURL: 'db url',  
  7.     projectId: 'project id',  
  8.     storageBucket: '',  
  9.     messagingSenderId: 'sender id'  
  10.   }};  

Now, first initialize your application with Firestore configuration in the module.ts file, as shown below.
  1. import { environment } from '../environments/environment';  
  2. import { AngularFireModule } from 'angularfire2';  
  3. import { AngularFirestoreModule } from 'angularfire2/firestore';  
  4.   
  5. imports: [  
  6.     BrowserModule,  
  7.     AngularFireModule.initializeApp(environment.firebase),  
  8.     AngularFirestoreModule  
  9.   ],  

First, import AngularFireModule and AngularFirestoreModule from AngularFire2 and then, initialize your application in imports array with the configuration which we had stored in environment.ts.

Now, let us create the service to fetch the data from Firestore database. And also, create a user class to provide some data structure. We can generate the service using the following command.

ng g s firestore-data

user.ts

  1. export class User {  
  2. public constructor(public id: string,public firstname: string,public lastname: string,public mobile_no: string) {  
  3.   }  
  4. }  

firestore-data.service.ts

Import the following packages to fetch the data from the server and store it in our observable.

  1. import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';  
  2.   
  3. import { Observable } from 'rxjs/Observable';  
  4. import { User } from './user';  

Now, create a collection which is type of our user class.

users: Observable<User[]>;

Then, create the instance of AngularFirestore in constructor. And, write the following code to fetch the data from user collection.

  1. this.users = this._afs.collection('Users').valueChanges();  

Collection without id

cloud Firestore

From the above valueChanges() method, we can fetch the data but not that auto-id. Which we will require to delete and update the record. So, to fetch that auto-id field with data, we should use snapshotChanges(), as shown below.

  1. this.users = this._afs.collection('Users').snapshotChanges().map(  
  2.       changes => {  
  3.         return changes.map(  
  4.           a => {  
  5.             const data = a.payload.doc.data() as User;  
  6.             data.id = a.payload.doc.id;  
  7.             return data;  
  8.           });  
  9.       });  

Collection with id

cloud Firestore

Now, it is time to display our data on our app.component file. To do so, first, let me inject our firestore-data.service in app.component.ts file, as shown below.

  1. import { FirestoreDataService } from './firestore-data.service';  
  2. import { User } from './user';  
  3. arr: User[] = [];  
  4. constructor(public _data: FirestoreDataService) {   }  

And now, call your getUsers method inside ngOnInit method of component’s life cycle event hook, as shown below.

  1. ngOnInit() {  
  2.     this._data.getUsers().subscribe(  
  3.       (user: User[]) => {  
  4.         this.arr = user;  
  5.         console.log(this.arr);  
  6.       }  
  7.     );  
  8.   }  

And on app.component.html, let’s loop through the array and display all the users on our page.

  1. <div class="container"  *ngIf="arr.length>0;else noData">  
  2.   <ul *ngFor="let item of arr" class="list-group">  
  3.     <li class="list-group-item">  
  4.       <strong>{{item.firstname}}{{item.lastname}}</strong> : {{item.mobile_no}} </li>  
  5.   
  6.   </ul>  
  7.   
  8. </div>  
  9. <div class="container">  
  10. <ng-template #noData>  
  11.   <hr>  
  12.   <h5>There are no users to display</h5>  
  13. </ng-template>  
  14. </div>  

cloud Firestore

Add New User

For adding a new user to collection, we will need to adjust 3 things, (i) app.component.html (ii) app.component.ts (iii) firestore-data.service.ts

Html
  1. <div class="container">  
  2.   <h1>Add New User</h1>  
  3.   <form (ngSubmit)="userSubmit()" #addform="ngForm">  
  4.     <div class="form-group">  
  5.       <label for="firstname">FirstName</label>  
  6.       <input type="text" [(ngModel)]="model.firstname" name="firstname" class="form-control" id="firstname" required #firstname="ngModel">  
  7.     </div>  
  8.     <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger">  
  9.       Firstname is required  
  10.     </div>  
  11.     <div class="form-group">  
  12.       <label for="lastname">LastName</label>  
  13.       <input type="text" [(ngModel)]="model.lastname" name="lastname" class="form-control" id="lastname" required #lastname="ngModel">  
  14.     </div>  
  15.     <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger">  
  16.       Lastname is required  
  17.     </div>  
  18.     <div class="form-group">  
  19.       <label for="mobile_no">Mobile Number</label>  
  20.       <input type="text" [(ngModel)]="model.mobile_no" name="mobile_no" class="form-control" id="mobile_no" required #mobileno="ngModel">  
  21.     </div>  
  22.     <div [hidden]="mobileno.valid || mobileno.pristine" class="alert alert-danger">  
  23.       Mobile number is required  
  24.     </div>  
  25.   <button type="submit" class="btn btn-default form-control" [disabled]="!addform.form.valid">Add User</button>  
  26.   </form>  
  27. </div>  

TS
  1. model = { firstname: '', lastname: '', mobile_no: '' };  
  2. userSubmit() {  
  3.     this._data.addUser(this.model);  
  4.     this.model.firstname = '';  
  5.     this.model.lastname = '';  
  6.     this.model.mobile_no = ''; }  

Service

In our Service file, first, we need to create usersCollection which is an instance of AngularFirestoreCollection.

  1. userscollection: AngularFirestoreCollection<User>; 

And initialize this userscollection inside the constructor, as shown below.

  1. this.userscollection = this._afs.collection('Users', x => x.orderBy('firstname''asc'));  

Here, I have used orderBy operator also to sort the data by first name in ascending order.

Then finally, the addUser method is used.

  1. addUser(user) {  
  2.     this.userscollection.add(user);  
  3.   }  
Delete User

Again, in order to delete the particular user from the list, we will require to change the 3 files. (i) app.component.html (ii) app.component.ts (iii) firestore-data.service.ts

HTML

Add a Delete button to app.component.html, as shown below.
  1. <a (click)="onDelete(item)" >  
  2.         <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>  
  3.       </a>  

TS

Create onDelete() method in app.component.ts, as shown below.
  1. onDelete(user) {  
  2.     this._data.deleteUser(user);  
  3.   }  

Service

First, we will create an instance of AngularfirestoreDocument, as shown below.

userDoc: AngularFirestoreDocument<User>;

Then, create deleteUser method inside the firestore-data.service file.

  1. deleteUser(user) {  
  2.     this.userDoc = this._afs.doc(`Users/${user.id}`);  
  3.     this.userDoc.delete();  
  4.   }  

final firestore-data.service.ts

  1. import { Injectable } from '@angular/core';  
  2. import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';  
  3.   
  4. import { Observable } from 'rxjs/Observable';  
  5. import { User } from './user';  
  6.   
  7. @Injectable()  
  8. export class FirestoreDataService {  
  9.   userscollection: AngularFirestoreCollection<User>;  
  10.   users: Observable<User[]>;  
  11.   userDoc: AngularFirestoreDocument<User>;  
  12.   constructor(public _afs: AngularFirestore) {  
  13.     //this.users = this._afs.collection('Users').valueChanges();  
  14.   
  15.     this.userscollection = this._afs.collection('Users', x => x.orderBy('firstname''asc'));  
  16.     this.users = this.userscollection.snapshotChanges().map(  
  17.       changes => {  
  18.         return changes.map(  
  19.           a => {  
  20.             const data = a.payload.doc.data() as User;  
  21.             data.id = a.payload.doc.id;  
  22.             return data;  
  23.           });  
  24.       });  
  25.   }  
  26.   getUsers() {  
  27.     return this.users;  
  28.   }  
  29.   addUser(user) {  
  30.     this.userscollection.add(user);  
  31.   }  
  32.   deleteUser(user) {  
  33.     this.userDoc = this._afs.doc(`Users/${user.id}`);  
  34.     this.userDoc.delete();  
  35.   }  
  36. }  

final app.component.html

  1. <div class="container">  
  2.   <h1>Add New User</h1>  
  3.   <form (ngSubmit)="userSubmit()" #addform="ngForm">  
  4.     <div class="form-group">  
  5.       <label for="firstname">FirstName</label>  
  6.       <input type="text" [(ngModel)]="model.firstname" name="firstname" class="form-control" id="firstname" required #firstname="ngModel">  
  7.     </div>  
  8.     <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger">  
  9.       Firstname is required  
  10.     </div>  
  11.     <div class="form-group">  
  12.       <label for="lastname">LastName</label>  
  13.       <input type="text" [(ngModel)]="model.lastname" name="lastname" class="form-control" id="lastname" required #lastname="ngModel">  
  14.     </div>  
  15.     <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger">  
  16.       Lastname is required  
  17.   
  18.     </div>  
  19.     <div class="form-group">  
  20.       <label for="mobile_no">Mobile Number</label>  
  21.       <input type="text" [(ngModel)]="model.mobile_no" name="mobile_no" class="form-control" id="mobile_no" required #mobileno="ngModel">  
  22.     </div>  
  23.     <div [hidden]="mobileno.valid || mobileno.pristine" class="alert alert-danger">  
  24.       Mobile number is required  
  25.     </div>  
  26.     <button type="submit" class="btn btn-default form-control" [disabled]="!addform.form.valid">Add User</button>  
  27.  </form>  
  28. </div>  
  29. <hr>  
  30. <div class="container" *ngIf="arr.length>0;else noData">  
  31.   <ul *ngFor="let item of arr" class="list-group">  
  32.     <li class="list-group-item">  
  33.       <strong>{{item.firstname}}{{item.lastname}}</strong> : {{item.mobile_no}}  
  34.   
  35.       <a (click)="onDelete(item)" >  
  36.         <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>  
  37.       </a>  
  38.     </li>  
  39.   </ul>  
  40. </div>  
  41. <div class="container">  
  42.   <ng-template #noData>  
  43.     <hr>  
  44.     <h5>There are no users to display</h5>  
  45.   </ng-template>  
  46. </div>  

final app.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2. import { FirestoreDataService } from './firestore-data.service';  
  3. import { User } from './user';  
  4.   
  5. @Component({  
  6.   selector: 'app-root',  
  7.   templateUrl: './app.component.html',  
  8.   styleUrls: ['./app.component.css']  
  9. })  
  10. export class AppComponent implements OnInit {  
  11.   arr: User[] = [];  
  12.   model = { firstname: '', lastname: '', mobile_no: '' };  
  13.   ngOnInit() {  
  14.     this._data.getUsers().subscribe(  
  15.       (user: User[]) => {  
  16.         this.arr = user;  
  17.         console.log(this.arr);  
  18.       }  
  19.     );  
  20.   }  
  21.   constructor(public _data: FirestoreDataService) {  
  22.   }  
  23.   userSubmit() {  
  24.     this._data.addUser(this.model);  
  25.     this.model.firstname = '';  
  26.     this.model.lastname = '';  
  27.     this.model.mobile_no = '';  
  28.   }  
  29.   onDelete(user) {  
  30.     this._data.deleteUser(user);  
  31.   }  
  32. }  

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now