Angular 6 Firebase Tutorial: Firestore CRUD Web Application

by Didin J. on Oct 11, 2018 Angular 6 Firebase Tutorial: Firestore CRUD Web Application

The Comprehensive step by step tutorial on build Firestore CRUD (Create, Read, Update, Delete) Web Application using Angular 6 and Firebase

The Comprehensive step by step tutorial on build Firestore CRUD (Create, Read, Update, Delete) Web Application using Angular 6 and Firebase. This tutorial similar with previous Ionic 4 and Angular 6 tutorial that using Firebase Real-time Database. Now, we show you how to use Angular 6 with Firestore Database using Firebase module/library.


Table of Contents:


The following tools, framework and module are required for this tutorial:

We assume that you have downloaded and installed Node.js environment. Now, let's check the above requirement by open the terminal or Node command line then go to your projects folder. Type this command to check the latest Node and NPM version.

node -v
v8.12.0
npm -v
6.4.1

That's our Node and NPM versions, let's move to the main steps of the Angular 6 and Firebase Firestore tutorial.


1. Angular 6 Firebase Tutorial: Setup Firebase Firestore Database

Setup Google Firebase is very simple. Open your browser then go to Google Firebase Console and log in using your google account.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Firebase Console

Click `Add Project` button, name it as you like (ours: Djamware) and checks all checkboxes. Finally, click `Create Project` button then wait a few seconds and click the `Continue` button. You will be redirected to this page.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Firebase Project

Go to Develop menu on the left menu then click `Create Database` on Cloud Firestore page.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Security Rules

Choose `Start in test mode` then click `Enabled` button.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Firestore Database

Click `Add Collection` button to add the new collection for this tutorial. Fill collection ID (ours: 'boards') then click next. Add the required fields then click finish the wizard.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Firestore Collection

Now, the Firebase Firestore Database is ready to access. Make sure that your iOS and Android app match with the configuration files (json/plist).


2. Angular 6 Firebase Tutorial: Install Angular-CLI and Create Angular 6 Application

To install or upgrade the latest Angular 6 CLI, type this command in the terminal or Node command line.

sudo npm install -g @angular/cli

If you use windows, it might be not necessary to add `sudo`. Next, create a new Angular 6 Web Application using this Angular CLI command.

ng new angular6-firestore

Next, go to the newly created Angular 6 project folder.

cd angular6-firestore

Now, you run the new Angular 6 web application using your own host and default port.

ng serve --host 0.0.0.0

Open your browser then go to `localhost:4200` you will see this Angular 6 page.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Angular 6 Welcome Page


3. Angular 6 Firebase Tutorial: Install and Configure Firebase Module

To access the Firebase Firestore Database, we just need to install the Firebase module. Type this command to install it.

npm install --save firebase

Next, register the Firebase module in the Angular 6 app by open and edit this file `src/app/app.component.ts` then add this import.

import * as firebase from 'firebase';
import firestore from 'firebase/firestore';

Declare a constant variable for holds Firebase and Firestore setting before `@Component`.

const settings = {timestampsInSnapshots: true};
const config = {
  apiKey: 'YOUR_APIKEY',
  authDomain: 'YOUR_AUTH_DOMAIN',
  databaseURL: 'YOUR_DATABASE_URL',
  projectId: 'YOUR_PROJECT_ID',
  storageBucket: 'YOUR_STORAGE_BUCKET',
};

Add `ngOnInit` Angular 6 builtin function while call the Firebase and Firestore config.

ngOnInit() {
  firebase.initializeApp(config);
  firebase.firestore().settings(settings);
}

Now, the Firebase is ready to use within the Angular 6 Application.


4. Angular 6 Firebase Tutorial: Create Angular 6 Routes

To create Angular 6 Routes for navigation between Angular 6 pages/component, add or generate all required component.

ng g component boards
ng g component boards-detail
ng g component boards-create
ng g component boards-edit

Next, open and edit again `src/app/app.module.ts` then add this import.

import { RouterModule, Routes } from '@angular/router';

Add this lines of codes for Angular routes before `@NgModule`.

const appRoutes: Routes = [
  {
    path: 'boards',
    component: BoardsComponent,
    data: { title: 'Boards List' }
  },
  {
    path: 'boards-details/:id',
    component: BoardsDetailComponent,
    data: { title: 'Boards Details' }
  },
  {
    path: 'boards-create',
    component: BoardsCreateComponent,
    data: { title: 'Create Boards' }
  },
  {
    path: 'boards-edit/:id',
    component: BoardsEditComponent,
    data: { title: 'Edit Boards' }
  },
  { path: '',
    redirectTo: '/boards',
    pathMatch: 'full'
  }
];

In @NgModule imports, section adds ROUTES constant, so the imports section will be like this.

imports: [
  RouterModule.forRoot(appRoutes),
  BrowserModule
],

To activate that routes in Angular 6, open and edit `src/app/app.component.html` then replace all codes with this.

<div style="text-align:center">
  <h1>
    Angular 6 Firebase
  </h1>
  <img width="150" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<router-outlet></router-outlet>


5. Angular 6 Firebase Tutorial: Install and Configure Angular 6 Material

For user interface (UI) we will use Angular 6 Material. There's a new feature for generating a Material component like Table as a component, but we will create or add the Table component from scratch to existing component. Type this command to install Angular Material.

ng add @angular/material

We will register all required Angular Material components or modules to `app.module.ts`. Open and edit that file then add this imports.

import {
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule } from "@angular/material";

Also, modify `FormsModule` import to add `ReactiveFormsModule`.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Register the above modules to `@NgModule` imports.

imports: [
  RouterModule.forRoot(appRoutes),
  BrowserModule,
  BrowserAnimationsModule,
  FormsModule,
  ReactiveFormsModule,
  BrowserAnimationsModule,
  MatInputModule,
  MatTableModule,
  MatPaginatorModule,
  MatSortModule,
  MatProgressSpinnerModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule
],


6. Angular 6 Firebase Tutorial: Create Angular 6 Service for CRUD Operation

The CRUD (Create, Read, Update, Delete) operation to Firebase Firestore Database are through Angular 6 Service. For that, generate Angular 6 Service by type this command.

ng g service Fs

Open and edit `src/app/fs.service.ts` then add this imports.

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as firebase from 'firebase';
import firestore from 'firebase/firestore'

Declare a variable for accessing Firestore collection before the constructor.

ref = firebase.firestore().collection('boards');

Add all of these CRUD functions.

getBoards(): Observable<any> {
  return new Observable((observer) => {
    this.ref.onSnapshot((querySnapshot) => {
      let boards = [];
      querySnapshot.forEach((doc) => {
        let data = doc.data();
        boards.push({
          key: doc.id,
          title: data.title,
          description: data.description,
          author: data.author
        });
      });
      observer.next(boards);
    });
  });
}

getBoard(id: string): Observable<any> {
  return new Observable((observer) => {
    this.ref.doc(id).get().then((doc) => {
      let data = doc.data();
      observer.next({
        key: doc.id,
        title: data.title,
        description: data.description,
        author: data.author
      });
    });
  });
}

postBoards(data): Observable<any> {
  return new Observable((observer) => {
    this.ref.add(data).then((doc) => {
      observer.next({
        key: doc.id,
      });
    });
  });
}

updateBoards(id: string, data): Observable<any> {
  return new Observable((observer) => {
    this.ref.doc(id).set(data).then(() => {
      observer.next();
    });
  });
}

deleteBoards(id: string): Observable<{}> {
  return new Observable((observer) => {
    this.ref.doc(id).delete().then(() => {
      observer.next();
    });
  });
}


7. Angular 6 Firebase Tutorial: Create List of Boards

To display list of boards, open and edit `src/app/boards/boards.component.ts` then add this imports.

import { DataSource } from '@angular/cdk/collections';
import { FsService } from '../fs.service';

Declare these variables and inject `FsService` to the constructor.

displayedColumns = ['title', 'description', 'author'];
dataSource = new BoardDataSource(this.fs);

constructor(private fs: FsService) {
}

Create a new class below the existing class.

export class BoardDataSource extends DataSource<any> {

  constructor(private fs: FsService) {
    super()
  }

  connect() {
    return this.fs.getBoards();
  }

  disconnect() {

  }
}

Next, open and edit `src/app/boards/boards.component.html` then replace all HTML tags with this tags.

<div class="button-row">
  <a mat-fab color="primary" [routerLink]="['/boards-create']"><mat-icon>add</mat-icon></a>
</div>
<div class="example-container mat-elevation-z8">
  <table mat-table #table [dataSource]="dataSource">

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- Title Column -->
    <ng-container matColumnDef="title">
      <th mat-header-cell *matHeaderCellDef> Title </th>
      <td mat-cell *matCellDef="let element" class="title-col"> {{element.title}} </td>
    </ng-container>

    <!-- Description Column -->
    <ng-container matColumnDef="description">
      <th mat-header-cell *matHeaderCellDef> Description </th>
      <td mat-cell *matCellDef="let element"> {{element.description}} </td>
    </ng-container>

    <!-- Author Column -->
    <ng-container matColumnDef="author">
      <th mat-header-cell *matHeaderCellDef> Author </th>
      <td mat-cell *matCellDef="let element"> {{element.author}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/boards-details/', row.key]"></tr>
  </table>
</div>

Finally, give the styles to for this page by open and edit `src/app/boards/boards.components.css` then replace all CSS codes with this.

.example-container {
  display: flex;
  flex-direction: column;
  max-height: 500px;
  min-width: 300px;
  overflow: auto;
}

.title-col {
  flex: 0 0 100px !important;
  white-space: unset !important;
}

.button-row {
  margin: 10px 0;
}


8. Angular 6 Firebase Tutorial: Show Details and Delete Board

To show boards details after click or tap on the one of a row inside the Angular Material table, open and edit `src/boards-detail/boards-detail.component.ts` then add this imports.

import { ActivatedRoute, Router } from '@angular/router';
import { FsService } from '../fs.service';

Inject above modules to the constructor.

constructor(private route: ActivatedRoute, private router: Router, private fs: FsService) { }

Declare a variable before the constructor for hold board data that get from the API.

board = {};

Add a function for getting a single Board data from the Firebase Service.

getBoardDetails(id) {
  this.fs.getBoard(id)
    .subscribe(data => {
      console.log(data);
      this.board = data;
    });
}

Call that function when the component is initiated.

ngOnInit() {
  this.getBoardDetails(this.route.snapshot.params['id']);
}

Next, add the function for delete board by ID.

deleteBoard(id) {
  this.fs.deleteBoards(id)
    .subscribe(res => {
        this.router.navigate(['/boards']);
      }, (err) => {
        console.log(err);
      }
    );
}

For the view, open and edit `src/board-detail/board-detail.component.html` then replace all HTML tags with this.

<div class="button-row">
  <a mat-fab color="primary" [routerLink]="['/boards']"><mat-icon>list</mat-icon></a>
</div>
<mat-card class="example-card">
  <mat-card-header>
    <mat-card-title><h2>{{board.title}}</h2></mat-card-title>
    <mat-card-subtitle>{{board.description}}</mat-card-subtitle>
  </mat-card-header>
  <mat-card-content>
    <dl>
      <dt>ISBN:</dt>
      <dd>{{board.isbn}}</dd>
      <dt>Author:</dt>
      <dd>{{board.author}}</dd>
      <dt>Publisher:</dt>
      <dd>{{board.publisher}}</dd>
      <dt>Publish Year:</dt>
      <dd>{{board.published_year}}</dd>
      <dt>Update Date:</dt>
      <dd>{{board.updated_date | date}}</dd>
    </dl>
  </mat-card-content>
  <mat-card-actions>
    <a mat-fab color="primary" [routerLink]="['/board-edit', board.key]"><mat-icon>edit</mat-icon></a>
    <a mat-fab color="warn" (click)="deleteBoard(board.key)"><mat-icon>delete</mat-icon></a>
  </mat-card-actions>
</mat-card>

Finally, open and edit `src/boards-detail/boards-detail.component.css` then add this lines of CSS codes.

.example-card {
  max-width: 500px;
}

.button-row {
  margin: 10px 0;
}


9. Angular 6 Firebase Tutorial: Add Board

To create a form for adding a Board, open and edit `src/boards-create/boards-create.component.ts` then add this imports.

import { Router } from '@angular/router';
import { FsService } from '../fs.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';

Inject above modules to the constructor.

constructor(private router: Router, private fs: FsService, private formBuilder: FormBuilder) { }

Declare variables for the Form Group and all of the required fields inside the form before the constructor.

boardsForm: FormGroup;
title:string='';
description:string='';
author:string='';

Add initial validation for each field.

ngOnInit() {
  this.boardsForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'description' : [null, Validators.required],
    'author' : [null, Validators.required]
  });
}

Create a function for submitting or POST board form.

onFormSubmit(form:NgForm) {
  this.fs.postBoards(form)
    .subscribe(res => {
        let id = res['key'];
        this.router.navigate(['/boards-details', id]);
      }, (err) => {
        console.log(err);
      });
}

Next, open and edit `src/boards-create/boards-create.component.html` then replace all HTML tags with this.

<div class="button-row">
  <a mat-fab color="primary" [routerLink]="['/boards']"><mat-icon>list</mat-icon></a>
</div>
<form [formGroup]="boardsForm" (ngSubmit)="onFormSubmit(boardsForm.value)">
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Title" formControlName="title"
           [errorStateMatcher]="matcher">
    <mat-error>
      <span *ngIf="!boardsForm.get('title').valid && boardsForm.get('title').touched">Please enter Board Title</span>
    </mat-error>
  </mat-form-field>
  <mat-form-field class="example-full-width">
    <textarea matInput placeholder="Description" formControlName="description"
           [errorStateMatcher]="matcher"></textarea>
    <mat-error>
      <span *ngIf="!boardsForm.get('description').valid && boardsForm.get('description').touched">Please enter Board Description</span>
    </mat-error>
  </mat-form-field>
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Author" formControlName="author"
           [errorStateMatcher]="matcher">
    <mat-error>
      <span *ngIf="!boardsForm.get('author').valid && boardsForm.get('author').touched">Please enter Board Author</span>
    </mat-error>
  </mat-form-field>
  <div class="button-row">
    <button type="submit" [disabled]="!boardsForm.valid" mat-fab color="primary"><mat-icon>save</mat-icon></button>
  </div>
</form>

Finally, open and edit `src/boards-create/boards-create.component.css` then add this CSS codes.

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child() {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}


10. Angular 6 Firebase Tutorial: Edit Board

We have put an edit button inside the Board Detail component. Now, open and edit `src/boards-edit/boards-edit.component.ts` then add this imports.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FsService } from '../fs.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';

Inject above modules to the constructor.

constructor(private router: Router, private route: ActivatedRoute, private fs: FsService, private formBuilder: FormBuilder) { }

Declare the Form Group variable and all of the required variables for the board form before the constructor.

boardsForm: FormGroup;
id:string = '';
title:string = '';
description:string = '';
author:string = '';

Next, add validation for all fields when the component is initiated.

ngOnInit() {
  this.getBoard(this.route.snapshot.params['id']);
  this.boardsForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'description' : [null, Validators.required],
    'author' : [null, Validators.required]
  });
}

Create a function for getting boards data that filled to each form fields.

getBoard(id) {
  this.fs.getBoard(id).subscribe(data => {
    this.id = data.key;
    this.boardsForm.setValue({
      title: data.title,
      description: data.description,
      author: data.author
    });
  });
}

Create a function to update the board changes.

onFormSubmit(form:NgForm) {
  this.fs.updateBoards(this.id, form)
    .subscribe(res => {
        this.router.navigate(['/boards']);
      }, (err) => {
        console.log(err);
      }
    );
}

Add a function for handling the show board details button.

boardsDetails() {
  this.router.navigate(['/boards-details', this.id]);
}

Next, open and edit `src/boards-edit/boards-edit.component.html` then replace all HTML tags with this.

<div class="button-row">
  <a mat-fab color="primary" (click)="boardsDetails()"><mat-icon>show</mat-icon></a>
</div>
<form [formGroup]="boardsForm" (ngSubmit)="onFormSubmit(boardsForm.value)">
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Title" formControlName="title"
           [errorStateMatcher]="matcher">
    <mat-error>
      <span *ngIf="!boardsForm.get('title').valid && boardsForm.get('title').touched">Please enter Book Title</span>
    </mat-error>
  </mat-form-field>
  <mat-form-field class="example-full-width">
    <textarea matInput placeholder="Description" formControlName="description"
           [errorStateMatcher]="matcher"></textarea>
    <mat-error>
      <span *ngIf="!boardsForm.get('description').valid && boardsForm.get('description').touched">Please enter Book Description</span>
    </mat-error>
  </mat-form-field>
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Author" formControlName="author"
           [errorStateMatcher]="matcher">
    <mat-error>
      <span *ngIf="!boardsForm.get('author').valid && boardsForm.get('author').touched">Please enter Book Author</span>
    </mat-error>
  </mat-form-field>
  <div class="button-row">
    <button type="submit" [disabled]="!boardsForm.valid" mat-fab color="primary"><mat-icon>save</mat-icon></button>
  </div>
</form>

Finally, open and edit `src/boards-edit/boards-edit.component.css` then add this lines of CSS codes.

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child() {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}


11. Angular 6 Firebase Tutorial: Run and Test The Application

Now, it's a time for testing the Angular 6 Firebase Firestore CRUD Web Application. Type this command again to run the application.

ng serve --host 0.0.0.0

In the browser go to this URL `localhost:4200` and here the whole application looks like.

Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Board List
Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Board Details
Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Edit Board
Angular 6 Firebase Tutorial: Firestore CRUD Web Application - Create Board

That it's, the Angular 6 Firebase Tutorial: Firestore CRUD Web Application. You can find the full working source code from our GitHub.

That just the basic. If you need more deep learning about MEAN Stack, Angular, and Node.js, you can take the following cheap course:

Thanks!

The following resources might be useful for you:

Loading…