Streamlining Task Management: Building a Dynamic Task List with Firebase Database and CRUD Operations (Part 14) in Your Angular-15 Ionic-7 App

Welcome back to our ongoing series on building a multiplatform application with Angular-15 and Ionic-7! In our previous articles, we have covered a wide range of topics, from authentication to data management. Today, in Part 14, we will explore the creation of a dynamic task list within our app, complete with CRUD (Create, Read, Update, Delete) operations, using Firebase Database.

Task management is a common feature in many applications, and by integrating Firebase Database, we can effortlessly create a dynamic task list that enables users to organize, track, and update their tasks seamlessly. With the powerful combination of Angular-15's framework and Ionic-7's versatility, we can build an app that offers a user-friendly and efficient task management experience.


In this installment, we will guide you through the process of integrating the Firebase Database into our Angular-15 Ionic-7 app to create a dynamic task list. We'll start by setting up the Firebase project and initializing the Firebase Database. Next, we'll design the user interface for the task list, allowing users to view and interact with their tasks.

But we won't stop there. We'll delve into the implementation details, demonstrating how to perform CRUD operations on tasks using Firebase Database. You'll learn how to create new tasks, fetch existing tasks, update task details, and delete unwanted tasks. This functionality will empower your users to effectively manage their tasks within the app.

Throughout this tutorial, we'll emphasize best practices for task organization, user feedback, and error handling. By the end of this article, you'll have a solid understanding of how to leverage Firebase Database to create a dynamic task list with seamless CRUD operations in your Angular-15 Ionic-7 app.

So, join us in Part 14 of our series as we embark on a journey into the world of task management. Together, let's empower our app with the ability to create, update, and delete tasks, offering users a seamless and efficient task management experience. Get ready to take your app's functionality and user experience to the next level!

Tutorial

In this section, I will create the feature Task List where students will be able to enter the tasks they need to do and the tasks they’ve already completed. First, I will begin to add the logic to communicate with the previously set up Firebase cloud database in the already created diary-data.service.ts file in the following way:

import { Injectable } from '@angular/core';
import {
 Firestore, collectionData, collection, docData, doc, addDoc, deleteDoc, updateDoc, DocumentData, getDocs, } from '@angular/fire/firestore';
import { Observable, map } from 'rxjs';

export interface Task {
  id?: string;
  title: string;
  text: string;
  done: boolean;
  timestamp?: string;
}

@Injectable({
  providedIn: 'root',
})
export class DiaryDataService {
  constructor(private firestore: Firestore) {} // inject the firestore service

//getTasks from the firestore and filter the tasks that are not done yet and return them as an observable
  getTasks(): Observable<Task[]> {
    // return an observable of type Task[]
    const tasksRef = collection(this.firestore, 'tasks'); // create a reference to the tasks collection
    const taskQuery = query(tasksRef, where('done', '==', false)); // filter the tasks that are not done yet
    const taskOptions = { idField: 'id' }; // added the idField option to the collectionData operator to get the id of the document
    return collectionData(taskQuery, taskOptions) as Observable<Task[]>; // return the tasks that are not done yet
  }

  // get Task by id from the firestore
  getTaskById(id: string): Observable<Task> {
    // return an observable of type Task
    const taskDocRef = doc(this.firestore, `tasks/${id}`); // create a reference to the task document
    return docData(taskDocRef, { idField: 'id' }) as Observable<Task>; // return the task by id
  }

  // add Task to the firestore and mark it as not done
  addTask(task: Task) {
    const tasksRef = collection(this.firestore, 'tasks'); // create a reference to the tasks collection
    // return addDoc(tasksRef, { ...task, done: false }); // add the task to the firestore and mark it as not done
    //add the task to the firestore
    return addDoc(tasksRef, task);
  }

  // delete task from the firestore
  deleteTaskById(task: Task) {
    const taskDocRef = doc(this.firestore, `tasks/${task.id}`); // create a reference to the task document
    return deleteDoc(taskDocRef); // delete the task from the firestore
  }

  // update task details like the title and text in the firestore
  updateSingleTaskById(task: Task) {
    const taskDocRef = doc(this.firestore, `tasks/${task.id}`); // create a reference to the task document
    return updateDoc(taskDocRef, {
      // update the task in the firestore with the new title and text passed in the task object
      title: task.title,
      text: task.text,
      done: task.done, // update the done status of the task
    }); // update the task in the firestore with the new title and done passed in the task object
  }

  //get done tasks from the firestore and return them as an observable of type Task[] to be displayed in the done tasks section
  getDoneTasks(): Observable<Task[]> {
    // return an observable of type Task[]
    const tasksRef = collection(this.firestore, 'tasks'); // create a reference to the tasks collection
    const taskQuery = query(tasksRef, where('done', '==', true)); // filter the tasks that are done already
    const taskOptions = { idField: 'id' }; // added the idField option to the collectionData operator to get the id of the document
    return collectionData(taskQuery, taskOptions) as Observable<Task[]>; // return the tasks that are done already
  }

  //update the done status of the task in the firestore by passing the task id and event to toggle the done status of the task in the firestore
  updateTaskById(taskId: string, event: any) {
    const taskDocRef = doc(this.firestore, `tasks/${taskId}`); // create a reference to the task document
    return updateDoc(taskDocRef, { done: event.detail.checked }); // update the done status of the task in the firestore // remember to pass the event.detail.checked to the updateDoc method and not just event.detail
  }

  //delete all the tasks that are done from the firestore by passing the task id and event to toggle the done status of the task in the firestore
  async deleteDoneTasks() {
    const tasksRef = collection(this.firestore, 'tasks'); // create a reference to the tasks collection
    const taskQuery = query(tasksRef, where('done', '==', true)); // filter the tasks that are done already
    return getDocs(taskQuery).then((querySnapshot) => {
      // get the tasks that are done already
      querySnapshot.forEach((doc) => {
        // loop through the tasks that are done already
        deleteDoc(doc.ref); // delete the task from the firestore
      });
    });
  }}

Here's an overview of what each method in the above code does:

  • constructor(private firestore: Firestore) {}: The constructor initializes the class with an instance of Firestore that is injected as a dependency.
  • getTasks(): Observable<Task[]>: Retrieves a list of tasks from Firestore and returns an observable of type Task[]. It uses the collection() function to create a reference to the "tasks" collection in Firestore, applies a query to filter out tasks that are marked as done, and returns an observable of the remaining tasks using the collectionData() function.
  • getTaskById(id: string): Observable<Task>: Retrieves a single task by its ID from Firestore and returns an observable of type Task. It uses the doc() function to create a reference to the specific task document in Firestore and then returns the data for that document using the docData() function.
  • addTask(task: Task): Adds a new task to Firestore. It uses the collection() function to create a reference to the "tasks" collection in Firestore and the addDoc() function to add the task to that collection.
  • deleteTaskById(task: Task): Deletes a task from Firestore. It uses the doc() function to create a reference to the specific task document in Firestore and then deletes that document using the deleteDoc() function.
  • updateSingleTaskById(task: Task): Updates a single task in Firestore with new details. It uses the doc() function to create a reference to the specific task document in Firestore and then updates the title, text, and done status of that document using the updateDoc() function.
  • getDoneTasks(): Observable<Task[]>: Retrieves a list of tasks that are marked as done from Firestore and returns an observable of type Task[]. It works similar to getTasks(), but applies a different query to filter out tasks that are not marked as done.
  • updateTaskById(taskId: string, event: any): Updates the done status of a single task in Firestore. It uses the doc() function to create a reference to the specific task document in Firestore and then updates the done status of that document based on the value of event.detail.checked. This took me some trial and error to figure out.
  • deleteDoneTasks(): Deletes all tasks that are marked as done from Firestore. It works similarly to, but retrieves the documents for each task using the getDocs() function and then deletes them one by one using the deleteDoc() function.

Next, I will import the service logic into the diary.page.ts which I use for displaying the tasks on the diary page in the following way:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {
  AlertController,IonicModule,ModalController,ToastController,
} from '@ionic/angular';
import { DiaryDataService } from 'src/app/services/diary-data.service';
import { RouterLink } from '@angular/router';
import { DiaryTaskModalPage } from '../diary-task-modal/diary-task-modal.page';
import { ItemReorderEventDetail } from '@ionic/angular';

@Component({
  selector: 'app-diary',
  templateUrl: './diary.page.html',
  styleUrls: ['./diary.page.scss'],
  standalone: true,
  imports: [
    IonicModule, CommonModule, FormsModule, DiaryModalPage, RouterLink,
    DiaryTaskModalPage,
  ],
})
export class DiaryPage implements OnInit {
  selectTabs: string = 'notes'; // set the default tab

  checked: boolean = false; // set the default value of the checked property to false
  tasks: any = []; // array of tasks
  doneTasks: any = []; // array of done tasks

  // Ion Reorder Group Event referred from original ionic documentation
  handleReorder(ev: CustomEvent<ItemReorderEventDetail>) {
    // The `from` and `to` properties contain the index of the item
    // when the drag started and ended, respectively
    // console.log('Dragged from index', ev.detail.from, 'to', ev.detail.to);

    // Finish the reorder and position the item in the DOM based on
    // where the gesture ended. This method can also be called directly
    // by the reorder group
    ev.detail.complete();
  }

  constructor(
    private diaryDataService: DiaryDataService,
    private alertCtrl: AlertController,
    private modalCtrl: ModalController,
    private toastCtrl: ToastController,
    private loadingCtrl: LoadingController
  ) {

    this.diaryDataService.getTasks().subscribe((res) => {
      // get the tasks from the diary data service
      // console.log(res);
      // subscribe to the tasks observable
      this.tasks = res; // assign the tasks property to the array of tasks returned by the observable
    });

    this.diaryDataService.getDoneTasks().subscribe((res) => {
      // get the done tasks from the diary data service
      // console.log(res);
      // subscribe to the done tasks observable
      this.doneTasks = res; // assign the done tasks property to the array of done tasks returned by the observable
    });
  }
  ngOnInit() {}

 

  //----------------------Task----------------------//

  //open task modal to update task details
  async openTask(task: any) {
    // open the task
    const modal = await this.modalCtrl.create({
      // create a modal
      component: DiaryTaskModalPage, // set the modal component
      componentProps: { id: task.id }, // pass the id of the task to the modal
      breakpoints: [0, 0.75, 1], // set the breakpoints
      initialBreakpoint: 0.75, // set the initial breakpoint
    }); // create a modal

    modal.present(); // present the modal
  }

  // add new task to the list
  async addTask() {
    // add a task via alert controller
    const alert = await this.alertCtrl.create({
      // create an alert
      header: 'Add Task', // set the header
      inputs: [
        // array of inputs
        {
          name: 'title', // input name
          type: 'text', // input type
          placeholder: 'Enter Your Task Title', // input placeholder
        },
        {
          name: 'text', // input name
          type: 'textarea', // input type
          placeholder: 'I need to learn react tomorrow...', // input placeholder
        },
      ],
      buttons: [
        // array of buttons
        {
          text: 'Cancel', // button text
          role: 'cancel', // set to cancel role
          cssClass: 'secondary', // set a css class for buttons
          handler: () => {
            // button handler
            console.log('Confirm Cancel');
          },
        },
        {
          text: 'Save', // button text
          handler: (task) => {
            // button handler
            // console.log(task);
            //get the current date and time
            //create a standard date time format
            const standardDateTimeFormat = new Intl.DateTimeFormat('en-US', {
              year: 'numeric',
              month: '2-digit',
              day: '2-digit',
              hour: '2-digit',
              minute: '2-digit',
              second: '2-digit',
            });

            const currentDate = new Date(); // get the current date and time

            const timestamp = standardDateTimeFormat.format(currentDate); // format the date and time to a standard format and assign it to the timestamp variable

            this.diaryDataService.addTask({
              title: task.title,
              text: task.text,
              timestamp: timestamp, // add timestamp property to the note object
              done: false, // set the done property to false
            }); // add the task to the firestore

            // console.log(task);
            const toast = this.toastCtrl.create({
              // create a toast
              message: 'Task Added', // set the message
              duration: 2000, // set the duration
            }); // create a toast
            toast.then((toast: any) => toast.present()); // present the toast
          },
        },
      ],
    }); // create an alert
    alert.present(); // present the alert
  }

  // update the done property of the task
  async toggleDone(id: string, event: any) {
    // console.log(id);
    // console.log(event.detail.checked); // get the checked property of the event
    this.diaryDataService.updateTaskById(id, event); // update the done property of the task

    //create a toast saying the task is completed

    if (event.detail.checked == true) {
      // if the task is marked as done create a toast saying the task is completed
      const toast = this.toastCtrl.create({
        // create a toast
        message: 'Task Completed', // set the message
        duration: 2000, // set the duration
      }); // create a toast
      toast.then((toast: any) => toast.present()); // present the toast
    } else if (event.detail.checked == false) {
      // if the task is marked as undone create a toast saying the task is undone
      const toast = this.toastCtrl.create({
        // create a toast
        message: 'Task Pending', // set the message
        duration: 2000, // set the duration
      }); // create a toast
      toast.then((toast: any) => toast.present()); // present the toast
    }
  }

  //clear all completed tasks from completed tasks list
  clearAllCheckedTasks() {
    this.diaryDataService.deleteDoneTasks(); // delete all the tasks that are marked as done
    const toast = this.toastCtrl.create({
      // create a toast
      message: 'All Completed Tasks Cleared', // set the message
      duration: 2000, // set the duration
    }); // create a toast
    toast.then((toast: any) => toast.present()); // present the toast
  }
}

In the above code, I have initiated the properties: tasks: an array that holds all tasks added by the user; checked: a boolean that indicates whether a task is completed or not. Its default value is false; and doneTasks: an array that holds all completed tasks.

The constructor function subscribes to the getTasks() and getDoneTasks() methods of the DiaryDataService, which are called whenever the DiaryPage component is created. When these methods are called, the tasks and doneTasks properties are updated with the arrays of tasks and done tasks retrieved from the Firestore database.

The handleReorder(ev: CustomEvent<ItemReorderEventDetail>) method handles a reorder event triggered by the user. It receives a CustomEvent object as input and calls its complete() method to complete the reorder and position the item in the DOM. This is used to implement the reorder functionality of the list items referenced from Reorder | ion-reorder: Drag and Drop Icon to Reorder Items (ionicframework.com).

The openTask(task: any)method opens a modal to update a task's details. It receives a task object as input and creates a modal using the ModalController module, passing the task's id as a parameter. The modal component used is DiaryTaskModalPage. This is created using the command: ionic generate page pages/diary-task and also imported into app.component.ts file.

The addTask() method adds a new task to the list. It creates an alert using the AlertController module with two input fields (title and text) and two buttons (Cancel and Save). When the Save button is clicked, the function gets the current date and time, formats it to a standard date time format, and adds the task object to the Firestore database using the addTask() method of the DiaryDataService. Finally, a toast is created using the ToastController module to confirm that the task has been added to the list.

The toggleDone(id: string, event: any) method updates the done property of a task when the user marks it as completed via the Ionic Checkbox. It receives the id and event of the task as input and calls the updateTaskById() method of the DiaryDataService to update the task's done property in the Firestore database. If the event.detail.checked property is true, indicating that the task is completed, a toast is created using the ToastController module to confirm that the task has been completed.

Next, I will implement this logic on the diary.page.html to display the tasks, along with add new button in the following way:

<ion-header [translucent]="false">
  <ion-toolbar>
    <ion-title>My Diary</ion-title>
    <ion-buttons slot="start">
      <ion-button routerLink="/tabs/notifications">
        <ion-icon slot="icon-only" name="notifications-outline"></ion-icon>
      </ion-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-menu-button menu="main-menu"></ion-menu-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">My Diary</ion-title>
    </ion-toolbar>
  </ion-header>

  <!-- Added a segment to the page to allow the user to switch between different pages -->

  <!-- Added segments from ionic framework to segment the page into three separate pages all accessible via a segment nav on top -->

  <ion-segment [(ngModel)]="selectTabs">
    <!-- ngModel is used to bind the value of the segment to the selectTabs variable -->
    <ion-segment-button value="schedule">
      <ion-label>Schedule</ion-label>
      <ion-icon name="calendar-outline"></ion-icon>
    </ion-segment-button>

    <ion-segment-button value="tasks">
      <ion-label>Tasks</ion-label>
      <ion-icon name="checkbox-outline"></ion-icon>
    </ion-segment-button>

    <ion-segment-button value="notes">
      <ion-label>Notes</ion-label>
      <ion-icon name="document-text-outline"></ion-icon>
    </ion-segment-button>
  </ion-segment>

  
  <div *ngIf="selectTabs === 'tasks'">
    <ion-list lines="full">
      <ion-list-header>
        <ion-label>
          Pending <ion-badge color="primary">{{tasks.length}}</ion-badge>
        </ion-label>
        <ion-button (click)="addTask()">Add New</ion-button>
      </ion-list-header>
      <!-- The reorder gesture is disabled by default, enable it to drag and drop items -->
      <!-- Casting $event to $any is a temporary fix for this bug <https://github.com/ionic-team/ionic-framework/issues/24245> -->
      <ion-reorder-group
        [disabled]="false"
        (ionItemReorder)="handleReorder($any($event))"
      >
        <ion-item *ngFor="let task of tasks">
          <ion-label (click)="openTask(task)">{{task.title}}</ion-label>
          <ion-checkbox
            (ionChange)="toggleDone(task.id, $any($event))"
            slot="start"
            [checked]="task.done"
            aria-label="task"
          ></ion-checkbox>
          <ion-reorder slot="end"></ion-reorder>
        </ion-item>
      </ion-reorder-group>
    </ion-list>
    <ion-list lines="full">
      <ion-list-header>
        <ion-label>
          Completed <ion-badge color="primary">{{doneTasks.length}}</ion-badge>
        </ion-label>

        <ion-button (click)="clearAllCheckedTasks()">Clear All</ion-button>
      </ion-list-header>
      <!-- The reorder gesture is disabled by default, enable it to drag and drop items -->
      <!-- Casting $event to $any is a temporary fix for this bug <https://github.com/ionic-team/ionic-framework/issues/24245> -->
      <ion-reorder-group
        [disabled]="false"
        (ionItemReorder)="handleReorder($any($event))"
      >
        <ion-item *ngFor="let task of doneTasks">
          <ion-label
            [style.color]="task.done ? 'grey' : 'inherit'"
            [style.textDecoration]="task.done ? 'line-through' : 'none'"
            (click)="openTask(task)"
            >{{task.title}}</ion-label
          >
          <ion-checkbox
            aria-label="task"
            slot="start"
            [checked]="task.done"
            (ionChange)="toggleDone(task.id, $any($event))"
          ></ion-checkbox>
          <ion-reorder slot="end"></ion-reorder>
        </ion-item>
      </ion-reorder-group>
    </ion-list>
  </div>
  
  <ion-fab slot="fixed" vertical="bottom" horizontal="end">
    <ion-fab-button>
      <ion-icon name="add"></ion-icon>
    </ion-fab-button>
    <ion-fab-list side="top">
      <ion-fab-button (click)="addNewEvent()" color="tertiary" size="large">
        <ion-icon name="calendar-outline"></ion-icon>
      </ion-fab-button>
      <ion-fab-button (click)="addTask()" color="tertiary" size="large">
        <ion-icon name="checkbox-outline"></ion-icon>
      </ion-fab-button>
      <ion-fab-button color="tertiary" size="large" (click)="addNote()">
        <ion-icon name="document-text-outline"></ion-icon>
      </ion-fab-button>
    </ion-fab-list>
  </ion-fab>
</ion-content>

The above template code defines two lists, one for pending tasks and another for completed tasks, using the <ion-list> element. The number of tasks in each list is displayed using an <ion-badge> element with the Angular interpolation syntax ({{}}) which is referred from Badges | ion-badge: iOS & Android App Notification Badge Icons (ionicframework.com).

Both lists use the <ion-reorder-group> element to allow the user to drag and drop items to reorder them. The disabled attribute is set to false to enable this feature. The ionItemReorder event is handled by calling the handleReorder function with the $event argument.

Each item in both lists is represented by an <ion-item> element. The task's title is displayed using an <ion-label> element, which is clickable and opens the task details when clicked. A checkbox element with the ionChange event bound to the toggleDone function is used to mark the task as done or not done.

In the completed tasks list, the <ion-label> element has additional styling applied using the Angular binding syntax ([style.color] and [style.textDecoration]) to gray out and add a line-through effect to the task title if it's marked as done.

Next, I will write the logic within the diary-task-modal.page.ts file for the task update modal in the following way:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { DiaryDataService } from 'src/app/services/diary-data.service';
import { ModalController, ToastController } from '@ionic/angular';
import { Input } from '@angular/core';
import { Task } from 'src/app/services/diary-data.service';

@Component({
  selector: 'app-diary-task-modal',
  templateUrl: './diary-task-modal.page.html',
  styleUrls: ['./diary-task-modal.page.scss'],
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule],
})
export class DiaryTaskModalPage implements OnInit {
  @Input() id!: string; // id of the task taken from the modal component props
  // input decorator is used here to pass the id of the task to the modal component
  task!: Task; // task object

  constructor(
    private diaryDataService: DiaryDataService,
    private modalCtrl: ModalController,
    private toastCtrl: ToastController
  ) {}

  ngOnInit() {
    this.diaryDataService.getTaskById(this.id).subscribe((res) => {
      this.task = res; // assign the task property to the task returned by the observable
    });
  }

  //update the task details
  async updateTask() {
    this.diaryDataService.updateSingleTaskById(this.task); // update the task
    //create a toast
    const toast = await this.toastCtrl.create({
      message: 'Task Updated', // set the message
      duration: 2000, // set the duration
    });
    toast.present(); // present the toast
    this.modalCtrl.dismiss(); // dismiss the modal
  }

  //delete the task
  async deleteTask() {
    await this.diaryDataService.deleteTaskById(this.task);
    //create a toast
    const toast = await this.toastCtrl.create({
      message: 'Task Deleted', // set the message
      duration: 2000, // set the duration
    });
    toast.present(); // present the toast
    this.modalCtrl.dismiss(); // dismiss the modal
  }

  dismissModal() {
    //dismiss the modal
    this.modalCtrl.dismiss();
  }
}

In the above code, there’s an @Input() property id to receive the ID of a task passed in from the parent component. The constructor of this class takes in three parameters: diaryDataService, modalCtrl, and toastCtrl. The ngOnInit() method is called when the component is initialized and retrieves the task corresponding to the id input parameter using the getTaskById() method provided by the diaryDataService service. When the observable returned by getTaskById() emits a value, the res parameter contains the task data, which is assigned to the task property of the component.

The updateTask() method is called when the user wants to update the task, and it updates the task data using the updateSingleTaskById() method provided by the diaryDataService service. It then displays a toast notification using the toastCtrl service to confirm the update and dismisses the modal using the modalCtrl service.

The deleteTask() method is called when the user wants to delete the task. It deletes the task using the deleteTaskById() method provided by the diaryDataService service, displays a toast notification to confirm the deletion, and dismisses the modal.

Finally, the dismissModal() method is called when the user wants to dismiss the modal without updating or deleting the task. It simply dismisses the modal using the modalCtrl service.

Next, I will use the above logic to build the HTML template in diary-task-modal.page.html file in the following way:

<ion-header [translucent]="true"> </ion-header>

<ion-content [fullscreen]="true">
  <ion-list lines="full" *ngIf="task">
    <ion-item>
      <ion-label>{{task.timestamp}}</ion-label>
      <ion-icon slot="end" name="close" (click)="dismissModal()"></ion-icon>
    </ion-item>
    <ion-item>
      <ion-label position="stacked">Task</ion-label>
      <ion-input aria-label="title" [(ngModel)]="task.title"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label position="stacked">Details</ion-label>
      <ion-textarea
        aria-label="task"
        [(ngModel)]="task.text"
        rows="5"
      ></ion-textarea>
    </ion-item>
    <ion-item>
      <ion-label
        >{{task.done ? 'Task is Completed' : 'Task is Pending'}}</ion-label
      >
    </ion-item>

    <ion-button
      color="danger"
      class="ion-margin"
      shape="round"
      (click)="deleteTask()"
      expand="block"
    >
      <ion-icon slot="start" name="trash-outline"></ion-icon>
      Delete</ion-button
    >
    <ion-button
      shape="round"
      class="ion-margin"
      color="success"
      (click)="updateTask()"
      expand="block"
    >
      <ion-icon slot="start" name="save"></ion-icon> Update</ion-button
    >
  </ion-list>
</ion-content>

In the above code, the ion-list component is used to create a list of items in the modal page. The ngIf directive is used to conditionally render the list only if a task object is present. Inside the ion-list, there are four ion-item components.

The first ion-item displays the timestamp property of the task object and an ion-icon component with the "close" icon. Clicking on the icon calls the dismissModal() method on the component.

The second and third ion-item components create input fields for the title and text properties of the task object using ion-input and ion-textarea components, respectively. The [(ngModel)] directive is used to bind the input fields to the task object, allowing the user to modify the task details.

The fourth ion-item displays the status of the task, either "Task is Completed" or "Task is Pending", depending on the value of the done property of the task object.

Two ion-button components are used to create buttons for deleting and updating the task. The color attribute is set to "danger" for the delete button and "success" for the update button. Clicking on the buttons calls the deleteTask() and updateTask() methods on the component, respectively.

Finally, the working of above code is demonstrated in Figures 59, 60, 61 & 62 below:

Figure 59: Task List in Diary


Figure 60: Add New Task Alert Box


Figure 61: New Task added to pending tasks



Figure 62: Update & delete task via modal


Conclusion:

In this fourteenth installment of our series on building a multiplatform application with Angular-15 and Ionic-7, we explored the creation of a dynamic task list with CRUD operations using Firebase Database. By leveraging the power of Firebase Database, we built a robust task management system that enables users to seamlessly organize, update, and track their tasks.

Task management is a crucial feature in many applications, and by integrating Firebase Database into our Angular-15 Ionic-7 app, we empowered users with a user-friendly and efficient task management experience. Users can effortlessly create new tasks, view existing tasks, update task details, and delete unwanted tasks, all within the app.

Throughout this tutorial, we guided you through the process of setting up the Firebase project, initializing Firebase Database, and designing the user interface for the task list. We also covered the implementation details, demonstrating how to perform CRUD operations on tasks using Firebase Database.

We emphasized best practices for task organization, user feedback, and error handling to ensure a seamless and intuitive task management experience. By following these practices, you can create an app that not only enhances productivity but also provides a delightful user experience.

As you move forward, consider exploring additional features and functionalities that can enhance your task management system. You can incorporate features like task sorting, filtering, and task reminders to further improve the user experience. Stay updated with the latest updates from Firebase and Angular to leverage new enhancements and improvements that can enhance your app's task management capabilities.

We hope this tutorial has provided you with valuable insights and practical knowledge on integrating Firebase Database into your Angular-15 Ionic-7 app to create a dynamic task list with CRUD operations. By embracing the power of Firebase Database, you can create an app that offers a seamless and efficient task management experience for your users.

Thank you for joining us on this journey as we explored the fascinating world of app development with Angular-15, Ionic-7, and Firebase Database. Stay tuned for future installments where we will continue to explore new features and functionalities to make our app even more robust and user-friendly.

Happy coding!

Popular Posts

Perform CRUD (Create, Read, Update, Delete) Operations using PHP, MySQL and MAMP : Part 4. My First Angular-15 Ionic-7 App

Visualize Your Data: Showcasing Interactive Charts for Numerical Data using Charts JS Library (Part 23) in Your Angular-15 Ionic-7 App

How to Build a Unit Converter for Your Baking Needs with HTML, CSS & Vanilla JavaScript