What is the 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.
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 that map to values.
Collections
Documents live in collections, which are simply containers for documents.
Here, users_data is the collection, and the document name will be obtained by auto key value.
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 the Database tab.
- Select Cloud Firestore Database and create a project in test mode.
- Now, add a collection by clicking Add Collection.
Setting up Angular Application
- First, get the configuration to connect Firestore from Angular by clicking the "Setting" button.
- Now, store all the configuration information.
Environments.ts
- export const environment = {
- production: false,
- firebase: {
- apiKey: 'your key',
- authDomain: 'your auth domain',
- databaseURL: 'db url',
- projectId: 'project id',
- storageBucket: '',
- messagingSenderId: 'sender id'
- }};
Now, first initialize your application with Firestore configuration in the module.ts file, as shown below.
- import { environment } from '../environments/environment';
- import { AngularFireModule } from 'angularfire2';
- import { AngularFirestoreModule } from 'angularfire2/firestore';
-
- imports: [
- BrowserModule,
- AngularFireModule.initializeApp(environment.firebase),
- AngularFirestoreModule
- ],
First, import AngularFireModule and AngularFirestoreModule from AngularFire2 and then, initialize your application in import array with the configuration which we had stored in environment.ts.
Now, let us create the service to fetch the data from the 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
- export class User {
- public constructor(public id: string,public firstname: string,public lastname: string,public mobile_no: string) {
- }
- }
firestore-data.service.ts
Import the following packages to fetch the data from the server and store it in our observable.
- import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
-
- import { Observable } from 'rxjs/Observable';
- 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 the constructor. And, write the following code to fetch the data from user collection.
- this.users = this._afs.collection('Users').valueChanges();
Collection without id
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.
- this.users = this._afs.collection('Users').snapshotChanges().map(
- changes => {
- return changes.map(
- a => {
- const data = a.payload.doc.data() as User;
- data.id = a.payload.doc.id;
- return data;
- });
- });
Collection with id
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.
- import { FirestoreDataService } from './firestore-data.service';
- import { User } from './user';
- arr: User[] = [];
- constructor(public _data: FirestoreDataService) { }
And now, call your getUsers method inside ngOnInit method of component’s life cycle event hook, as shown below.
- ngOnInit() {
- this._data.getUsers().subscribe(
- (user: User[]) => {
- this.arr = user;
- console.log(this.arr);
- }
- );
- }
And on app.component.html, let’s loop through the array and display all the users on our page.
- <div class="container" *ngIf="arr.length>0;else noData">
- <ul *ngFor="let item of arr" class="list-group">
- <li class="list-group-item">
- <strong>{{item.firstname}}{{item.lastname}}</strong> : {{item.mobile_no}} </li>
-
- </ul>
-
- </div>
- <div class="container">
- <ng-template #noData>
- <hr>
- <h5>There are no users to display</h5>
- </ng-template>
- </div>
Add New User
For adding a new user to the collection, we will need to adjust 3 things, (i) app.component.html (ii) app.component.ts (iii) firestore-data.service.ts
Html
- <div class="container">
- <h1>Add New User</h1>
- <form (ngSubmit)="userSubmit()" #addform="ngForm">
- <div class="form-group">
- <label for="firstname">FirstName</label>
- <input type="text" [(ngModel)]="model.firstname" name="firstname" class="form-control" id="firstname" required #firstname="ngModel">
- </div>
- <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger">
- Firstname is required
- </div>
- <div class="form-group">
- <label for="lastname">LastName</label>
- <input type="text" [(ngModel)]="model.lastname" name="lastname" class="form-control" id="lastname" required #lastname="ngModel">
- </div>
- <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger">
- Lastname is required
- </div>
- <div class="form-group">
- <label for="mobile_no">Mobile Number</label>
- <input type="text" [(ngModel)]="model.mobile_no" name="mobile_no" class="form-control" id="mobile_no" required #mobileno="ngModel">
- </div>
- <div [hidden]="mobileno.valid || mobileno.pristine" class="alert alert-danger">
- Mobile number is required
- </div>
- <button type="submit" class="btn btn-default form-control" [disabled]="!addform.form.valid">Add User</button>
- </form>
- </div>
TS
- model = { firstname: '', lastname: '', mobile_no: '' };
- userSubmit() {
- this._data.addUser(this.model);
- this.model.firstname = '';
- this.model.lastname = '';
- this.model.mobile_no = ''; }
Service
In our Service file, first, we need to create usersCollection which is an instance of AngularFirestoreCollection.
- userscollection: AngularFirestoreCollection<User>;
And initialize this userscollection inside the constructor, as shown below.
- this.userscollection = this._afs.collection('Users', x => x.orderBy('firstname', 'asc'));
Here, I have used orderBy operator also to sort the data by the first name in ascending order.
Then finally, the addUser method is used.
- addUser(user) {
- this.userscollection.add(user);
- }
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.
- <a (click)="onDelete(item)" >
- <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
- </a>
TS
Create onDelete() method in app.component.ts, as shown below.
- onDelete(user) {
- this._data.deleteUser(user);
- }
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.
- deleteUser(user) {
- this.userDoc = this._afs.doc(`Users/${user.id}`);
- this.userDoc.delete();
- }
final firestore-data.service.ts
- import { Injectable } from '@angular/core';
- import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
-
- import { Observable } from 'rxjs/Observable';
- import { User } from './user';
-
- @Injectable()
- export class FirestoreDataService {
- userscollection: AngularFirestoreCollection<User>;
- users: Observable<User[]>;
- userDoc: AngularFirestoreDocument<User>;
- constructor(public _afs: AngularFirestore) {
-
-
- this.userscollection = this._afs.collection('Users', x => x.orderBy('firstname', 'asc'));
- this.users = this.userscollection.snapshotChanges().map(
- changes => {
- return changes.map(
- a => {
- const data = a.payload.doc.data() as User;
- data.id = a.payload.doc.id;
- return data;
- });
- });
- }
- getUsers() {
- return this.users;
- }
- addUser(user) {
- this.userscollection.add(user);
- }
- deleteUser(user) {
- this.userDoc = this._afs.doc(`Users/${user.id}`);
- this.userDoc.delete();
- }
- }
final app.component.html
- <div class="container">
- <h1>Add New User</h1>
- <form (ngSubmit)="userSubmit()" #addform="ngForm">
- <div class="form-group">
- <label for="firstname">FirstName</label>
- <input type="text" [(ngModel)]="model.firstname" name="firstname" class="form-control" id="firstname" required #firstname="ngModel">
- </div>
- <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger">
- Firstname is required
- </div>
- <div class="form-group">
- <label for="lastname">LastName</label>
- <input type="text" [(ngModel)]="model.lastname" name="lastname" class="form-control" id="lastname" required #lastname="ngModel">
- </div>
- <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger">
- Lastname is required
-
- </div>
- <div class="form-group">
- <label for="mobile_no">Mobile Number</label>
- <input type="text" [(ngModel)]="model.mobile_no" name="mobile_no" class="form-control" id="mobile_no" required #mobileno="ngModel">
- </div>
- <div [hidden]="mobileno.valid || mobileno.pristine" class="alert alert-danger">
- Mobile number is required
- </div>
- <button type="submit" class="btn btn-default form-control" [disabled]="!addform.form.valid">Add User</button>
- </form>
- </div>
- <hr>
- <div class="container" *ngIf="arr.length>0;else noData">
- <ul *ngFor="let item of arr" class="list-group">
- <li class="list-group-item">
- <strong>{{item.firstname}}{{item.lastname}}</strong> : {{item.mobile_no}}
-
- <a (click)="onDelete(item)" >
- <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
- </a>
- </li>
- </ul>
- </div>
- <div class="container">
- <ng-template #noData>
- <hr>
- <h5>There are no users to display</h5>
- </ng-template>
- </div>
final app.component.ts
- import { Component, OnInit } from '@angular/core';
- import { FirestoreDataService } from './firestore-data.service';
- import { User } from './user';
-
- @Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
- })
- export class AppComponent implements OnInit {
- arr: User[] = [];
- model = { firstname: '', lastname: '', mobile_no: '' };
- ngOnInit() {
- this._data.getUsers().subscribe(
- (user: User[]) => {
- this.arr = user;
- console.log(this.arr);
- }
- );
- }
- constructor(public _data: FirestoreDataService) {
- }
- userSubmit() {
- this._data.addUser(this.model);
- this.model.firstname = '';
- this.model.lastname = '';
- this.model.mobile_no = '';
- }
- onDelete(user) {
- this._data.deleteUser(user);
- }
- }