Angular 16 Firebase CRUD Operations with Reactive Forms

Last Updated on by in Angular
Angular 16 Firebase CRUD example, In this tutorial you will learn how to create CRUD Operations using Angular and Firebase real-time NoSQL cloud database.We’ll be using Angular’s Reactive Forms service for managing the user submitted data in our web application.

For the demo purpose, will be creating a basic student record management system in which a school admin can perform Create, Read, Update and Delete operations respectively.

Angular 16 Firebase CRUD Application Example

  • Install Angular App
  • Firebase Account Set up
  • Create Firebase CRUD Operations
  • Generate Angular Components
  • Add Routing for Navigation
  • Show Alert Messages with NGX Toaster
  • Add Data in Firebase Storage
  • Create Pagination
  • Fetch List Items and Delete
  • Create Edit

Install Angular App

Setup Node JS development environment:

Before we move ahead I’m assuming you already have Node JS development environment set up in your system.

Please follow this link How to Set up Node JS Development Environment?

Install Angular CLI, Ignore if Angular CLI is already installed.

npm install -g @angular/cli

Let’s set up a fresh new Angular project with Bootstrap 4 and Font Awesome for our basic student record management system CRUD app.

ng new angularfirebase-student-app

Once the project is installed then get into the project’s directory by following the following command.

cd angularfirebase-student-app

Now its time to setup Bootstrap CSS framework in your Angular project. Use the mentioned command to install the latest version of the Bootstrap framework.

npm install bootstrap

Go to angular.json file to register Bootstrap and FontAwesome CSS in styles array.

...
...
"styles": [
    "node_modules/bootstrap/dist/css/bootstrap.min.css",
    "src/styles.scss"
],
...
...

Disable Strict Type Errors

To avoid TypeScript compiling issues, we just need to open tsconfig.json file:

Set below property under “compilerOptions” property.

"compilerOptions": {
    "strict": false,
    ...
}

No Property Access From Index Signature

To resolve error:

Property ‘xxxName’ comes from an index signature, so it must be accessed with [‘xxxName’]

This setting makes sure profound consistency between accessing a field via the “dot” (obj.key) syntax, and “indexed” (obj["key"]) and the way which the property is declared in the type.

Without this flag, TypeScript will allow you to use the dot syntax to access fields which are not defined:

Make sure to set noPropertyAccessFromIndexSignature property to false under compilerOptions in tsconfig.json file:

"compilerOptions": {
// ...
// ...
   "noPropertyAccessFromIndexSignature": false,
// ...
// ...
}

Firebase Account Set up + AngularFire2 Library Integration

Go to Firebase website and login using your email id, when you see given below screen click on Add Project section.

Setup Google Firebase

Enter your project name, accept the terms and condition and click on Create project button.

Firebase setup

Click on your project then you’ll enter in your Firebase dashboard.

Firebase dashboard

Navigate to Develop > Authentication > Web setup then click on the Web setup button, and a popup will appear along with your firebase credentials.

Copy these Firebase credentials, you will have to paste these credentials in your src/environments/enviorment.ts file to make the connection between Firebase and your Angular app.

Firebase Details

Firebase offers Real-time Database and Cloud Firestore, for this tutorial we are going to use Real-time Database.

Next, click on create database and make sure to set the Firebase security rules to test mode.

Don’t forget to change your Firebase database rules, go to Database > Rules. Add these security rules in your Realtime Database’s Rules tab and then publish them.

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Note: Don’t forget to change these rules when you are building a real app for your clients.

Install Firebase and AngularFire2 Package

Run the given below command from the command prompt.

npm install @angular/fire firebase@9.16.0 --legacy-peer-deps

Connect the Angular application to Firebase database by adding firebase credentials into the environment file.

Add code in environment.ts file.

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "xxxxxx-xxxxxx_xxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    databaseURL: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    storageBucket: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxxxxxxxxxx",
    appId: "1:xxxxxxxxxxxxxxxxxx:web:xxxxxxxxxxxxxxxxxx"
  }
};

Open app.module.ts file and import the Firebase modules and environment file.

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

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

// Import Firebase modules + environment
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireAuthModule } from '@angular/fire/compat/auth';
import { AngularFireStorageModule } from '@angular/fire/compat/storage';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { AngularFireDatabaseModule } from '@angular/fire/compat/database';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule,
    AngularFireStorageModule,
    AngularFireDatabaseModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})

export class AppModule {}

Create CRUD operations with Firebase API

Before writing the CRUD operations we must create a new folder by the name of shared within app/shared and create crud.service.ts and student.ts interface class into it.

Run the following command to generate student interface class for setting up data types.

ng g i shared/student

Afterwards, update the code in app/shared/student.ts file.

export interface Student {
   $key: string;
   firstName: string;
   lastName: string;
   email: string
   mobileNumber: Number;
}

Run the following command to generate a CRUD service file.

ng g s shared/crud

Next, create CRUD operations using Firebase API, so add code in shared/crud.service.ts file.

import { Injectable } from '@angular/core';
import { Student } from '../shared/student';

import {
  AngularFireDatabase,
  AngularFireList,
  AngularFireObject,
} from '@angular/fire/compat/database';

@Injectable({
  providedIn: 'root',
})

export class CrudService {
  studentsRef: AngularFireList<any>;
  studentRef: AngularFireObject<any>;

  constructor(private db: AngularFireDatabase) {}

  // Create Student
  AddStudent(student: Student) {
    this.studentsRef.push({
      firstName: student.firstName,
      lastName: student.lastName,
      email: student.email,
      mobileNumber: student.mobileNumber,
    });
  }

  // Fetch Single Student Object
  GetStudent(id: string) {
    this.studentRef = this.db.object('students-list/' + id);
    return this.studentRef;
  }

  // Fetch Students List
  GetStudentsList() {
    this.studentsRef = this.db.list('students-list');
    return this.studentsRef;
  }

  // Update Student Object
  UpdateStudent(student: Student) {
    this.studentRef.update({
      firstName: student.firstName,
      lastName: student.lastName,
      email: student.email,
      mobileNumber: student.mobileNumber,
    });
  }

  // Delete Student Object
  DeleteStudent(id: string) {
    this.studentRef = this.db.object('students-list/' + id);
    this.studentRef.remove();
  }
}

Generate Angular Components

In the next step, we need to create components for adding, updating and creating data in angular.

ng g c add-student

ng g c edit-student

ng g c student-list

Now we are able to write our app logic in these components.

Set Up Router for Navigation

In this step, you need to open and place the given below code in app-routing.modules.ts file.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

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

import { AddStudentComponent } from './add-student/add-student.component';
import { StudentListComponent } from './student-list/student-list.component';
import { EditStudentComponent } from './edit-student/edit-student.component';

const routes: Routes = [
  { path: '', redirectTo: '/register-student', pathMatch: 'full' },
  { path: 'register-student', component: AddStudentComponent },
  { path: 'view-students', component: StudentListComponent },
  { path: 'edit-student/:id', component: EditStudentComponent }
];

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

export class AppRoutingModule { }

Let us configure the routes in the app to enable the navigation so, open app.component.html file and add the given below code.

<!-- Top navigation -->
<nav class="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
  <a class="navbar-brand col-sm-3 col-md-2 mr-0" routerLink="/register-student">
    <span class="dasboard-text">Dashboard</span>
  </a>
  <ul class="navbar-nav px-3">
    <li class="nav-item text-nowrap">
      <a class="nav-link" routerLink="/register-student">
        <span class="user-image" style="background-image: url('assets/user.jpg')"></span>
        Hello Admin
      </a>
    </li>
  </ul>
</nav>

<!-- Sidebar navigation -->
<div class="container-fluid">
  <div class="row">
    <nav class="col-md-2 d-md-block bg-light sidebar">
      <div class="sidebar-sticky">
        <ul class="nav flex-column">
          <li class="nav-item">
            <a class="nav-link" routerLink="/register-student" routerLinkActive="active">
              Add Student
            </a>
          </li>

          <li class="nav-item">
            <a class="nav-link" routerLink="/view-students" routerLinkActive="active">
              Students List
            </a>
          </li>
        </ul>
      </div>
    </nav>

    <!-- Main content -->
    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
      <div class="inner-adjust">
        <router-outlet></router-outlet>
      </div>
    </main>

  </div>
</div>

Show Alert Messages

We’ll be requiring NGX Toastr NPM module to show alert messages when an update occurs in student’s data. In order to install NGX Toastr, we’ll be using the following command.

npm install ngx-toastr --save

npm install @angular/animations --save

Then go to angular.json and add the following code in styles array.

"styles": [
  "node_modules/ngx-toastr/toastr.css"
]

Add NGX Toastr css path in app.moudule.ts file.

// Import below modules for NGX Toastr
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
 
@NgModule({
  imports: [
    BrowserAnimationsModule, // required animations module
    ToastrModule.forRoot() // ToastrModule added
  ]
})
class MainModule {}

Import Reactive Forms Module

Import FormsModule, AND ReactiveFormsModulee within the app.module.ts file.

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

@NgModule({
  imports: [
    FormsModule,
    ReactiveFormsModule
  ]
]

Create the student form for adding data, also use getter method to access the form object to show errors. So, open and add the add-student.component.html file.

<div class="pt-3 pb-2 mb-3 border-bottom">
  <h2 class="h2">Add Student</h2>
</div>

<form [formGroup]="studentForm" (ngSubmit)="submitStudentData()" novalidate>
  <div class="row">
    <div class="col-lg-5 col-md-12 col-sm-12">
      <div class="row">
        <div class="col-md-12 mb-3">
          <label>First name</label>
          <input
            type="text"
            formControlName="firstName"
            class="form-control"
            required
          />

          <!-- errors-->
          <p *ngIf="firstName.touched && firstName.invalid" class="error">
            <sup>*</sup>Please enter atleast first name
          </p>
          <p *ngIf="firstName.errors?.['minlength']" class="error">
            <sup>*</sup>Name shouldn't be less than 2 words
          </p>
        </div>

        <div class="col-md-12 mb-3">
          <label>Last name</label>
          <input type="text" formControlName="lastName" class="form-control" />
        </div>
      </div>

      <div class="row">
        <div class="col-md-12 mb-3">
          <label>Email</label>
          <input
            type="email"
            formControlName="email"
            class="form-control"
            required
          />

          <!-- errors-->
          <p *ngIf="email.touched && email.invalid" class="error">
            <sup>*</sup>Please provide email
          </p>
          <p *ngIf="email.errors?.['pattern']" class="error">
            <sup>*</sup>Please enter correct email
          </p>
        </div>

        <div class="col-md-12 mb-3">
          <label>Mobile number</label>
          <input
            type="text"
            formControlName="mobileNumber"
            class="form-control"
            required
          />

          <!-- errors-->
          <p *ngIf="mobileNumber.touched && mobileNumber.invalid" class="error">
            <sup>*</sup>Please provide contact number
          </p>
          <p *ngIf="mobileNumber.errors?.['pattern']" class="error">
            <sup>*</sup>Use numbers only number
          </p>
        </div>
      </div>

      <div class="form-group text-right">
        <button
          type="button"
          class="btn btn-secondary gap-right"
          (click)="ResetForm()"
        >
          Reset
        </button>
        <button
          type="submit"
          class="btn btn-success"
          [disabled]="!studentForm.valid"
        >
          Add Student
        </button>
      </div>
    </div>
  </div>
</form>

Afterwards, add code in add-student.component.ts file.

import { Component, OnInit } from '@angular/core';
import { CrudService } from '../shared/crud.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-add-student',
  templateUrl: './add-student.component.html',
  styleUrls: ['./add-student.component.scss'],
})

export class AddStudentComponent implements OnInit {
  public studentForm: FormGroup;

  constructor(
    public crudApi: CrudService,
    public fb: FormBuilder,
    public toastr: ToastrService
  ) {}

  ngOnInit() {
    this.crudApi.GetStudentsList();
    this.studenForm();
  }

  studenForm() {
    this.studentForm = this.fb.group({
      firstName: ['', [Validators.required, Validators.minLength(2)]],
      lastName: [''],
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$'),
        ],
      ],
      mobileNumber: ['', [Validators.required, Validators.pattern('^[0-9]+$')]],
    });
  }

  get firstName() {
    return this.studentForm.get('firstName');
  }

  get lastName() {
    return this.studentForm.get('lastName');
  }

  get email() {
    return this.studentForm.get('email');
  }

  get mobileNumber() {
    return this.studentForm.get('mobileNumber');
  }

  ResetForm() {
    this.studentForm.reset();
  }

  submitStudentData() {
    this.crudApi.AddStudent(this.studentForm.value);
    this.toastr.success(
      this.studentForm.controls['firstName'].value + ' successfully added!'
    );
    this.ResetForm();
  }
}

Import angular CRUD services, Form modules, and ToastrService at the top

Use the getter method to access the form control, similarly access GetStudentsList() method to get the data from the Firebase database. The submitStudentData() method is fired on form submit and add the data into the database.

Add NGX Pagination in Angular

Run below command in Angular CLI to install NGX Pagination NPM module.

npm install ngx-pagination --save

Open app.module.ts file and add the given below code.

// NGX Pagination
import { NgxPaginationModule } from 'ngx-pagination';

@NgModule({
    imports: [
          NgxPaginationModule  // Include it in imports array
     ] 
})

Fetch Data Collection and Delete

Let us get data collection from the database, also integrate pagination and delete feature in angular firebase crud app.

We are displaying the pre-loader before the data loads into the view.

Th*ngFor loop iterates over the Student array and fetch the student’s data.

The paginate pipe will add pagination in the student’s list, and it won’t show if items are less e than 7

Open and add code in student-list.component.html file.

<div
  class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
>
  <h2 class="h2">Students List</h2>
  <a
    routerLink="/register-student"
    class="btn btn-success"
    *ngIf="hideWhenNoStudent"
  >
    Add Student
  </a>
</div>

<div class="pricing-header mx-auto">
  <div class="no-data text-center" *ngIf="preLoader">
    <img src="assets/preloader.gif" class="preloader-icon" alt="No student" />
  </div>

  <div class="no-data text-center" *ngIf="noData">
    <img src="assets/no-student.svg" class="nodata-msg" alt="No student" />
    <p class="nodata-msg">No student added yet!</p>
    <a routerLink="/register-student" class="btn btn-success"> Add Student </a>
  </div>

  <div class="table-responsive" *ngIf="hideWhenNoStudent">
    <table
      class="table table-bordered table-responsive-sm table-responsive-md table-responsive-lg"
    >
      <thead>
        <tr>
          <th scope="col">Student Id</th>
          <th scope="col">Student name</th>
          <th scope="col">Email</th>
          <th scope="col">Mobile number</th>
          <th class="text-center" scope="col">Edit</th>
        </tr>
      </thead>
      <tbody>
        <tr
          *ngFor="
            let student of Student
              | paginate: { itemsPerPage: 8, currentPage: p };
            let i = index
          "
        >
          <th scope="row">{{ student.$key }}</th>
          <td>{{ student.firstName }} {{ student.lastName }}</td>
          <td>{{ student.email }}</td>
          <td>{{ student.mobileNumber }}</td>
          <td class="text-center action-block">
            <span
              class="mr-2 btn btn-outline-primary btn-sm"
              routerLink="/edit-student/{{ student.$key }}"
              >Edit</span
            >
            <span
              class="btn btn-outline-danger btn-sm"
              (click)="deleteStudent(student)"
              >Delete</span
            >
          </td>
        </tr>
      </tbody>
    </table>
  </div>

  <pagination-controls (pageChange)="p = $event"></pagination-controls>
</div>

Update or add code in student-list.component.ts file.

import { Component, OnInit } from '@angular/core';
import { CrudService } from '../shared/crud.service';
import { Student } from './../shared/student'; 
import { ToastrService } from 'ngx-toastr';


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

export class StudentListComponent implements OnInit {
  p: number = 1;
  Student: Student[];
  hideWhenNoStudent: boolean = false;
  noData: boolean = false;
  preLoader: boolean = true;
  

  constructor(
    public crudApi: CrudService,
    public toastr: ToastrService
    ){ }


  ngOnInit() {
    this.dataState();
    let s = this.crudApi.GetStudentsList(); 
    s.snapshotChanges().subscribe(data => {
      this.Student = [];
      data.forEach(item => {
        let a = item.payload.toJSON(); 
        a['$key'] = item.key;
        this.Student.push(a as Student);
      })
    })
  }

  dataState() {     
    this.crudApi.GetStudentsList().valueChanges().subscribe(data => {
      this.preLoader = false;
      if(data.length <= 0){
        this.hideWhenNoStudent = false;
        this.noData = true;
      } else {
        this.hideWhenNoStudent = true;
        this.noData = false;
      }
    })
  }

  deleteStudent(student) {
    if (window.confirm('Are sure you want to delete this student ?')) { 
      this.crudApi.DeleteStudent(student.$key)
      this.toastr.success(student.firstName + ' successfully deleted!');
    }
  }

}

Update or Edit

In the last step, we will create edit functionality for editing or updating student data object using Firebase CRUD services.

Create the edit form using HTML and Reactive Form’s attributes then add form validation block within HTML layout.

Open and insert code in edit-student.component.html file.

<div
  class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
>
  <h1 class="h2">Edit Student Details</h1>
  <div class="btn-toolbar mb-2 mb-md-0">
    <div class="btn-group">
      <button class="btn btn-sm btn-outline-secondary" (click)="goBack()">
        Go Back
      </button>
    </div>
  </div>
</div>

<div class="row">
  <div class="col-lg-12">
    <div class="pricing-header form-block mx-auto">
      <form [formGroup]="editForm" (ngSubmit)="updateForm()" novalidate>
        <div class="row">
          <div class="col-lg-5 col-md-12 col-sm-12">
            <div class="row">
              <div class="col-md-12 mb-3">
                <label>First name</label>
                <input
                  type="text"
                  formControlName="firstName"
                  class="form-control"
                  required
                />
                <p *ngIf="firstName.touched && firstName.invalid" class="error">
                  <sup>*</sup>Please enter firstname
                </p>
                <p *ngIf="firstName.errors?.['minlength']" class="error">
                  <sup>*</sup>Name shouldn't be less than 2 words
                </p>
              </div>
              <div class="col-md-12 mb-3">
                <label>Last name</label>
                <input
                  type="text"
                  formControlName="lastName"
                  class="form-control"
                />
              </div>
            </div>
            <div class="row">
              <div class="col-md-12 mb-3">
                <label>Email</label>
                <input
                  type="email"
                  formControlName="email"
                  class="form-control"
                  required
                />
                <p *ngIf="email.touched && email.invalid" class="error">
                  <sup>*</sup>Please provide email
                </p>
                <p *ngIf="email.errors?.['pattern']" class="error">
                  <sup>*</sup>Please enter correct email
                </p>
              </div>
              <div class="col-md-12 mb-3">
                <label>Mobile number</label>
                <input
                  type="text"
                  formControlName="mobileNumber"
                  class="form-control"
                  required
                />
                <p
                  *ngIf="mobileNumber.touched && mobileNumber.invalid"
                  class="error"
                >
                  <sup>*</sup>Please provide contact number
                </p>
                <p *ngIf="mobileNumber.errors?.['pattern']" class="error">
                  <sup>*</sup>Use numbers only number
                </p>
              </div>
            </div>
            <div class="form-group text-right">
              <button
                type="submit"
                class="btn btn-success btn-block"
                [disabled]="!editForm.valid"
              >
                Update Student
              </button>
            </div>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>

In the last step, you have to add the code in edit-student.component.ts file.

import { Component, OnInit, AfterViewInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { CrudService } from '../shared/crud.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Location } from '@angular/common';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-edit-student',
  templateUrl: './edit-student.component.html',
  styleUrls: ['./edit-student.component.scss'],
})

export class EditStudentComponent implements OnInit {
  editForm: FormGroup;

  constructor(
    private crudApi: CrudService,
    private fb: FormBuilder,
    private location: Location,
    private actRoute: ActivatedRoute,
    private router: Router,
    private toastr: ToastrService
  ) {}

  ngOnInit() {
    this.updateStudentData();
    const id = this.actRoute.snapshot.paramMap.get('id');
    this.crudApi
      .GetStudent(id)
      .valueChanges()
      .subscribe((data) => {
        this.editForm.setValue(data);
      });
  }

  get firstName() {
    return this.editForm.get('firstName');
  }

  get lastName() {
    return this.editForm.get('lastName');
  }

  get email() {
    return this.editForm.get('email');
  }

  get mobileNumber() {
    return this.editForm.get('mobileNumber');
  }

  updateStudentData() {
    this.editForm = this.fb.group({
      firstName: ['', [Validators.required, Validators.minLength(2)]],
      lastName: [''],
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$'),
        ],
      ],
      mobileNumber: ['', [Validators.required, Validators.pattern('^[0-9]+$')]],
    });
  }

  goBack() {
    this.location.back();
  }

  updateForm() {
    this.crudApi.UpdateStudent(this.editForm.value);
    this.toastr.success(
      this.editForm.controls['firstName'].value + ' updated successfully'
    );
    this.router.navigate(['view-students']);
  }
}

In the last step, you have to add the CSS in the src/styles.scss file.

body{font-size:.875rem;font-family:'Poppins', sans-serif;}
.feather{width:16px;height:16px;vertical-align:text-bottom;}

/* * Sidebar */
.sidebar{top:0;bottom:0;left:0;z-index:100;/* Behind the navbar */
 padding:48px 0 0;/* Height of navbar */
 box-shadow:inset -1px 0 0 rgba(0, 0, 0, .1);}
.sidebar-sticky{position:relative;top:0;height:calc(100vh - 48px);padding-top:.5rem;overflow-x:hidden;overflow-y:auto;}
@supports ((position:-webkit-sticky) or (position:sticky)){.sidebar-sticky{position:-webkit-sticky;position:sticky;}}
.sidebar .nav-link{font-weight:500;color:#333;}
.sidebar .nav-link .feather{margin-right:4px;color:#999;}
.sidebar .nav-link.active{color:#007bff;}
.sidebar .nav-link:hover .feather,.sidebar .nav-link.active .feather{color:inherit;}
.sidebar-heading{font-size:.75rem;text-transform:uppercase;}

/* * Content */
[role="main"]{padding-top:48px;}
.dasboard-text{border-left:1px solid rgb(255, 255, 255, .3);color:rgb(255, 255, 255, .5);display:inline-block;padding:0 0 0 14px;font-size:15px;margin-left:15px;position:relative;top:-1px;}

/* * Navbar */
.navbar-brand{padding-top:.75rem;padding-bottom:.75rem;}
.navbar .form-control{padding:.75rem 1rem;border-width:0;border-radius:0;}
.form-control-dark{color:#fff;background-color:rgba(255, 255, 255, .1);border-color:rgba(255, 255, 255, .1);}
.form-control-dark:focus{border-color:transparent;box-shadow:0 0 0 3px rgba(255, 255, 255, .25);}
.form-control:focus{border-color:#00BCD4;box-shadow:none;}
.form-control{font-size:14px;}
.bg-dark{background-color:#1633FF !important;}
.gap-right{margin-right:10px;}
i{width:22px;text-align:center;margin-right:5px;}
.inner-adjust{padding:0 20px;}
.action-block{cursor:pointer;}
.action-block .fa-edit:hover{color:#009688;}
.action-block .fa-trash-alt:hover{color:#E91E63;}
.btn-primary.focus,.btn-primary:focus{box-shadow:none;}

/* Pagination */
body pagination-template{padding:0;margin:8px 0 0;float:left;width:100%;text-align:right;}
body .ngx-pagination li:last-child{margin:0;}
body .ngx-pagination .current{background:#055AF9;}
.ngx-pagination a:hover,.ngx-pagination button:hover{text-decoration:none;}

/* Error */
.error{color:red;margin-top:5px;}
input.ng-invalid.ng-touched{border:1px solid red;}
.btn-success.disabled,.btn-success:disabled{cursor:not-allowed;}
#toast-container>div { opacity: 1; }

/* Nav */
body .navbar{padding:6px 0 !important;}
body .navbar-brand{background:none;}
.brand-logo{max-width:85%;}
.pt-3,.py-3{padding-top:2.4rem !important;}
.sidebar-sticky{padding-top:2.9rem !important;}

/* Form */
label{font-weight:500;}
.form-control{padding:1.375rem .75rem;}

/* Misc */
.no-data img{max-width:420px;margin:20px auto 0;}
.nodata-msg{margin:25px 0 15px;font-size:28px;color:#a9a6c5;font-weight:300;letter-spacing:.2px;}
[role="main"]{padding-top:65px;}
.preloader{min-height:400px;display:flex;align-items:center;justify-content:center;margin-top:-15px;}
.custom-text{font-size:15px;color:#5f5f5f;letter-spacing:.2px;}
.navbar-dark .navbar-brand{margin-left:6px;}
.custom-text strong{color:#3a3a3a;}
.mb-3,.my-3{margin-bottom:1.4rem !important;}
.custom-fa-plus{margin:0;width:auto;}
.user-image{width:42px;height:42px;display:inline-block;border-radius:50%;vertical-align:middle;margin-right:7px;background-size:cover;background-repeat:no-repeat;background-position:0 0;}
body .table thead th{background:#f3f5ff;}
.pricing-header { padding-bottom: 50px;}

/* Footer */
footer {width: 100%;text-align: center;padding-bottom: 25px;border-top: 1px solid #dee2e6 !important;padding-top: 25px; margin-top: 50px;}

/* Responsive */
@media(max-width:767px){.sidebar{position:static;padding:40px 0 10px;height:auto;}
 .sidebar-sticky{height:auto;}
 [role="main"]{padding-top:0;}
 .inner-adjust{padding:0;}
 ul.nav.flex-column{flex-direction:inherit !important;}
 .pt-3, .py-3{padding-top:1.5rem !important;}
 .brand-logo{max-width:175px;margin:0 auto;display:block;}
 .dasboard-text{display:none !important;}
 .sidebar-sticky .nav li {width: 50%;text-align: center;border-right: 1px solid #c7ceff;}
 .sidebar-sticky .nav li:last-child {border: none;}
 .no-data img {max-width: 100%; margin-top: 0;}
 .nodata-msg, .h2, h2 {font-size: 1.4rem;}
 .custom-text {font-size: 14px;}
 .navbar-nav {float: right;width: 50%;text-align: right;display: inherit;margin: 0;}
 .navbar-dark .navbar-brand {margin: 0;width: 50%;float: left;display: inherit;}
 .sidebar {padding: 40px 0 15px;}
 footer br { display: none; }
}

Here is the final app.module.ts file.

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

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

// Import Firebase modules + environment
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireAuthModule } from '@angular/fire/compat/auth';
import { AngularFireStorageModule } from '@angular/fire/compat/storage';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { AngularFireDatabaseModule } from '@angular/fire/compat/database';
import { environment } from '../environments/environment';
import { AddStudentComponent } from './add-student/add-student.component';
import { EditStudentComponent } from './edit-student/edit-student.component';
import { StudentListComponent } from './student-list/student-list.component';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
import { NgxPaginationModule } from 'ngx-pagination';

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


@NgModule({
  declarations: [AppComponent, AddStudentComponent, EditStudentComponent, StudentListComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule,
    AngularFireStorageModule,
    AngularFireDatabaseModule,
    BrowserAnimationsModule, // required animations module
    ToastrModule.forRoot(), // ToastrModule added
    NgxPaginationModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent],
})

export class AppModule {}
ng serve --open

We hope you will like this Angular Firebase CRUD example tutorial and you can download the final project code from GitHub.