Ionic 5 Firestore CRUD Operations using Firebase Tutorial

Ionic 5 Angular Firebase CRUD operations tutorial; this step-by-step example will profoundly explain how to create a mobile CRUD operations application in the Ionic 5 using Google Firebase NoSQL real-time database.

CRUD is a fundamental building block for every web and mobile application, and It stands for Create, Read, Update and Delete altogether. It is known as CRUD. Create stands for adding or creating data objects into the database. Reading means getting or retrieving data objects from the database then manifesting on the view; update refers to updating, altering, modifying the data, and delete utterly determines deleting the data object from the database.

This ionic crud tutorial will be a boon for those who are just getting started in their application development career and give them direction on dealing with Ionic 5 Firebase CRUD immaculately.

In this Ionic 5 Firebase CRUD example, we will throughly describe how to use Firestore to create CRUD operations for native mobile applications using Ionic and Angular frameworks.

Ionic 5 Angular CRUD Operations using Firebase Example

We will create an Ionic 5 todo app, and this will start with setting up an ionic, angular environment, adding firestore in ionic, comprises a connection between ionic Firebase.

Our Ionic Angular ToDo App will be a basic app that allows us to organize our day-to-day tasks with an easy-to-use interface. A user can create tasks, prepare a list of all the tasks, update a task, and delete a task. Here are the instructions that you need to follow to build the app.

  • Step 1: Setting Up New Ionic Project
  • Step 2: Create Firebase Project
  • Step 3: Install Firebase and AngularFire Packages
  • Step 4: Update Environment Files
  • Step 5: Update Routing Module
  • Step 6: Add Navigation Routes
  • Step 7: Create Angular Service
  • Step 8: Create
  • Step 9: Read and Delete
  • Step 10: Update
  • Step 11: Test Ionic App

Setting Up New Ionic Project

In the first step, you have to install Ionic CLI that lets you commence the ionic development on your system.

npm install -g @ionic/cli

Now, you begin the installation of Ionic Angular app:

ionic start ionic-firestore-crud-app blank --type=angular

Next, head over to app folder:

cd ionic-firestore-crud-app

To create the Todo CRUD app, we need to generate few pages, so execute the following commands from the terminal to create the new pages. We don’t need the Home page, so make sure to delete the default Home page component.

ng generate page create-todo

ng generate page update-todo

ng generate page todo-list

Create Firebase Project

In this step, we will learn how to create a new Firebase project to get the Firebase configuration keys. Also, use with the AngularFire package to invoke a connection between Ionic and Firestore.

You have to head over to Firebase website and open the Firebase console.

Next, in the “Create a project” give a name to your project.

Right after that, you have to get started by adding Firebase to your app, hence, click on the “Web icon”.

Thereafter, add Firebase to your web app screen manifests; you must provide the app nickname and click on the “Register App” button.

Then copy the Firebase SDK keys; these configuration keys will be needed shortly.

Further, click on Firestore, create a Cloud Firestore database for real-time updates, powerful queries, and automatic scaling. For the demo, the purpose select the “test mode”.

Install Firebase and AngularFire Packages

In this step, you have to add Firebase and AngularFire packages in Ionic app, accordingly execute the following command:

npm i firebase @angular/fire

Update Environment Files

Let us add the Firebase configuration keys in angular’s environment files, so consecutively add the Firebase keys in environment.ts and environment.prod.ts files.

// environment.prod.ts

export const environment = {
  production: true,
  firebaseConfig: {
    apiKey: "xxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxxxxx",
    storageBucket: "xxxxxxxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxx"
  }
};
// environment.ts

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "xxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxxxxx",
    storageBucket: "xxxxxxxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxx"
  }
};

Update App Module

Now that we have to add the AngularFire modules and environment variable in the main app module class, it creates the connection Firebase database connection with Ionic.

Update app.module.ts file:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

//  Firebase modules
import { AngularFireModule } from '@angular/fire';
import { AngularFireDatabaseModule } from '@angular/fire/database';

// Environment
import { environment } from '../environments/environment';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireDatabaseModule
  ],
  providers: [
    { 
      provide: RouteReuseStrategy, 
      useClass: IonicRouteStrategy 
    }
    ],
  bootstrap: [AppComponent],
})

export class AppModule {}

Update Routing Module

You have to make the little changes in the app routing file to handle the CRUD operations:

Update the app/app-routing.ts file:

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

const routes: Routes = [
  {
    path: '', 
    redirectTo: 'create-task', 
    pathMatch: 'full'
  },
  {
    path: 'create-task',
    loadChildren: () => import('./create-todo/create-todo.module').then( m => m.CreateTodoPageModule)
  },
  {
    path: 'update-todo/:id',
    loadChildren: () => import('./update-todo/update-todo.module').then( m => m.UpdateTodoPageModule)
  },
  {
    path: 'todo-list',
    loadChildren: () => import('./todo-list/todo-list.module').then( m => m.TodoListPageModule)
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})

export class AppRoutingModule { }

Add Navigation Routes

You require to define tab routes to navigate in the crud todo application, so open and update the following code in the app.component.html file.

<ion-app>
  <ion-router-outlet></ion-router-outlet>
</ion-app>

<!-- Navigation tabs -->
<ion-tabs>
  <ion-tab-bar slot="bottom">
    <ion-tab-button routerLinkActive="tab-selected" routerLink="/todo-list" tab="todo-list">
      <ion-icon name="list-outline"></ion-icon>
      <ion-label>Tasks</ion-label>
    </ion-tab-button>

    <ion-tab-button routerLinkActive="tab-selected" routerLink="/create-task" tab="create-task">
      <ion-icon name="add-circle-outline"></ion-icon>
      <ion-label>Create Task</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
</ion-tabs>

Create Angular Service

Subsequently, generate angular service to create the reusable component for handling the custom methods for CRUD operations.

ng generate service services/crud

Declare TODO class to define the schema for todo task, plus import AngularFirestore, inject it inside the constructor method. This allows you to access the Firestore methods, and you can easily create custom methods to make request to create CRUD operations.

Update services/crud.service.ts file:

import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Router } from "@angular/router";

export class TODO {
  $key: string;
  title: string;
  description: string;
}

@Injectable({
  providedIn: 'root'
})

export class CrudService {

  constructor(
    private ngFirestore: AngularFirestore,
    private router: Router
  ) { }

  create(todo: TODO) {
    return this.ngFirestore.collection('tasks').add(todo);
  }

  getTasks() {
    return this.ngFirestore.collection('tasks').snapshotChanges();
  }
  
  getTask(id) {
    return this.ngFirestore.collection('tasks').doc(id).valueChanges();
  }

  update(id, todo: TODO) {
    this.ngFirestore.collection('tasks').doc(id).update(todo)
      .then(() => {
        this.router.navigate(['/todo-list']);
      }).catch(error => console.log(error));;
  }

  delete(id: string) {
    this.ngFirestore.doc('tasks/' + id).delete();
  }

}

Create

To work with form component, you may take help of reactive forms, so import the ReactiveFormsModule in page specific module file. Hence add the code in create-todo.module.ts file:

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

@NgModule({
  imports: [
    ReactiveFormsModule
  ]
})

Create a simple form and add two form controls with title and description fields.

Update create-todo.page.html file:

<ion-header>
  <ion-toolbar>
    <ion-title>Create Task</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <form [formGroup]="todoForm" (ngSubmit)="onSubmit()">
    <ion-item>
      <ion-label position="floating">Title</ion-label>
      <ion-input formControlName="title" type="text" required></ion-input>
    </ion-item>

    <ion-item>
      <ion-label position="floating">Description</ion-label>
      <ion-input formControlName="description" type="text" required>
      </ion-input>
    </ion-item>

    <ion-button class="ion-margin-top" type="submit" expand="block" color="success">Create</ion-button>
  </form>
</ion-content>

Store the todo task in the Firestore database with create() method, then navigate to the todo list page with router API.

Update create-todo.page.ts file:

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

import { CrudService } from './../services/crud.service';
import { FormGroup, FormBuilder } from "@angular/forms";
import { Router } from '@angular/router';

@Component({
  selector: 'app-create-todo',
  templateUrl: './create-todo.page.html',
  styleUrls: ['./create-todo.page.scss'],
})

export class CreateTodoPage implements OnInit {

  todoForm: FormGroup;

  constructor(
    private crudService: CrudService,
    public formBuilder: FormBuilder,    
    private router: Router
  ) { }

  ngOnInit() {
    this.todoForm = this.formBuilder.group({
      title: [''],
      description: ['']
    })
  }

  onSubmit() {
    if (!this.todoForm.valid) {
      return false;
    } else {
      this.crudService.create(this.todoForm.value)
      .then(() => {
        this.todoForm.reset();
        this.router.navigate(['/todo-list']);
      }).catch((err) => {
        console.log(err)
      });
    }
  }

}

Read and Delete

In this step, we will fetch the tasks list likewise show you how to delete the task object from the Firestore. Use the ion-list UI component to show the ionic data list and add a button bind remove() function with a click event to remove the data object.

Update todo-list.page.html file:

<ion-header>
  <ion-toolbar>
    <ion-title>Todo List</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <ion-list>
    <ion-item *ngFor="let task of Tasks" lines="full">
      <ion-label>
        <strong>{{task.title}}</strong>
        <p>{{task.description}}</p>
      </ion-label>

      <div item-end>
        <button [routerLink]="['/update-todo/', task.id]">
          <ion-icon name="create" style="zoom:1.5"></ion-icon>
        </button>
        <button (click)="remove(task.id)">
          <ion-icon name="trash" style="zoom:1.5"></ion-icon>
        </button>
      </div>
    </ion-item>
  </ion-list>

</ion-content>

Update todo-list.page.ts file:

import { Component, OnInit } from '@angular/core';
import { CrudService } from './../services/crud.service';

export class TODO {
  $key: string;
  title: string;
  description: string;
}

@Component({
  selector: 'app-todo-list',
  templateUrl: './todo-list.page.html',
  styleUrls: ['./todo-list.page.scss'],
})

export class TodoListPage implements OnInit {

  Tasks: TODO[];

  constructor(private crudService: CrudService) { }

  ngOnInit() {
    this.crudService.getTasks().subscribe((res) => {
      this.Tasks = res.map((t) => {
        return {
          id: t.payload.doc.id,
          ...t.payload.doc.data() as TODO
        };
      })
    });
  }

  todoList() {
    this.crudService.getTasks()
    .subscribe((data) => {
      console.log(data)
    })
  }

  remove(id) {
    console.log(id)
    if (window.confirm('Are you sure?')) {
      this.crudService.delete(id)
    }
  }  

}

Update

In this final example, you will see how to get a single task object from the cloud firestore database using activated route api, and the custom update() method.

One more time, register reactive forms module into the update-todo.module.ts file:

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

@NgModule({
  imports: [
    ReactiveFormsModule
  ]
})

Update update-todo.page.html file:

<ion-header>
  <ion-toolbar>
    <ion-title>Update Task</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <form [formGroup]="editForm" (ngSubmit)="onSubmit()">
    <ion-item>
      <ion-label position="floating">Title</ion-label>
      <ion-input formControlName="title" type="text" required></ion-input>
    </ion-item>

    <ion-item>
      <ion-label position="floating">Description</ion-label>
      <ion-input formControlName="description" type="text" required>
      </ion-input>
    </ion-item>

    <ion-button class="ion-margin-top" type="submit" expand="block" color="success">Update</ion-button>
  </form>
</ion-content>

Update update-todo.page.ts file:

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

import { CrudService } from './../services/crud.service';
import { Router, ActivatedRoute } from "@angular/router";
import { FormGroup, FormBuilder } from "@angular/forms";



@Component({
  selector: 'app-update-todo',
  templateUrl: './update-todo.page.html',
  styleUrls: ['./update-todo.page.scss'],
})

export class UpdateTodoPage implements OnInit {

  editForm: FormGroup;
  id: any;

  constructor(
    private crudService: CrudService,
    private activatedRoute: ActivatedRoute,
    private router: Router,
    public formBuilder: FormBuilder
  ) {
    this.id = this.activatedRoute.snapshot.paramMap.get('id');
    this.crudService.getTask(this.id).subscribe((data) => {
      this.editForm = this.formBuilder.group({
        title: [data['title']],
        description: [data['description']]
      })
    });
  }

  ngOnInit() {
    this.editForm = this.formBuilder.group({
      title: [''],
      description: ['']
    })    
  }

  onSubmit() {
    this.crudService.update(this.id, this.editForm.value)
  }

}

Test Ionic App

Eventually, we are ready to test our crud mobile application, so head over to command prompt and follow the given instructions:

Firstly, add the platform in which you want to run the app:

# iOS
ionic cordova platform add ios

# Android
ionic cordova platform add android

# Windows
ionic cordova platform add windows

Thereafter, create the runnable build:

# iOS
ionic cordova build ios

# Android
ionic cordova build android

# Windows
ionic cordova build windows

Ultimately, run app on the device:

# iOS
ionic cordova run ios -l

# Android
ionic cordova run android -l

# Windows
ionic cordova run windows -l

Ionic Firestore CRUD App

Conclusion

The Ionic Firestore Mobile CRUD operations tutorial is over; we had a chance to build an excellent Todo crud application in Ionic Angular for the native mobile device using the Firebase NoSQL database.

Grab the final code from GitHub: