Angular Tutorial: Creating Firebase Chat Web App

by Didin J. on Apr 26, 2025 Angular Tutorial: Creating Firebase Chat Web App

A comprehensive step by step Angular tutorial on creating Firebase chat web app with the quick user login, room list, and chat room with online users list

In this Angular tutorial, we will gonna show you a chat web app that uses Firebase Realtime Database. This chat web app has the features of quick user login and creation by nickname, room list, and chat room with the online users' list. The flow is very simple as described in this diagram.

Angular 9 Tutorial: Creating Firebase Chat Web App - flow diagram

The first entry of this Angular application is the login page. In the login page, it will check if the nickname exists in the local storage. If the nickname exists, it will go to the room list directly, and if not exist, it will stay on the login page to enter the nickname in the login form. The request from the login form will compare with the user's document in the Firebase Realtime Database. If the nickname exists in the Firebase document, then it will go to the room list and set the nickname in the local storage. If the nickname does not exist in the Firebase, then it will save the nickname first to the users' document before going to the room list and setting it in the local storage. The room list contains a list of the chat room, a current user nickname, an add the room button, and a logout button. The add button action will go to the add room form, then compare the submitted room name to the Firebase Realtime Database document. If the room name exists, it will return an alert, and if not exist, it will save a new room name to the Firebase document. The new room will automatically appear on the room list. The logout button action will log out and exit the room list by removing the nickname from the local storage and redirecting the page to the login page. Each room item in the room list has an action to go to the chat room. In the chat room, there is a chat box containing the message, a message form, the online users' lists, and an exit button that has an action to go back to the room list.

The following tools, frameworks, libraries, and modules are required for this tutorial:

  1. Node.js and NPM
  2. Angular CLI
  3. Angular Material
  4. Firebase Module
  5. Google Firebase
  6. Terminal (Mac/Linux) or Node Command Line (Windows)
  7. IDE or Text Editor (We are using Visual Studio Code)

You can watch the video tutorial on our YouTube channel. If you like it, please like, share, comment, and subscribe to this channel.

Let's get started with the main steps!


Step #1: Set up Firebase Realtime Database

We will set up or create a new Google Firebase project that can use the Realtime Database. Just open your browser, then go to Google Firebase Console, and you will be taken to this page.

Angular 9 Tutorial: Creating Firebase Chat Web App - firebase welcome

From that page, click the Create Project button to create a Google Firebase project then it will be redirected to this page.

Angular 9 Tutorial: Creating Firebase Chat Web App  - Project name

After filling the project name text field, which our project name is "AngularChat", and selecting your parent resources to your domain (ours: "djamware.com"), then click the continue button, and it will be redirected to this page.

Angular 9 Tutorial: Creating Firebase Chat Web App - Firebase analytics

This time, choose not to add Firebase analytics for now, then click the  Create Project button. Now, you have a Google Firebase Project ready to use.

Angular 9 Tutorial: Creating Firebase Chat Web App - Firebase ready

After clicking the Continue button, it will be redirected to this page.

Angular 9 Tutorial: Creating Firebase Chat Web App - project dashboard

Choose the Database menu in the left pane, then click the Create Database button.

Angular 9 Tutorial: Creating Firebase Chat Web App - create database

Select "Start in test mode" then click next, and it will go to the next dialog.

Angular 9 Tutorial: Creating Firebase Chat Web App - database location

Select the Firebase database server location (better near your Angular server location), then click the Done button. Don't forget to select or change Cloud Firestore to Realtime Database in Develop -> Database dashboard. Next, go to the Rules tab, and you will see these rule values.

{
  /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
  "rules": {
    ".read": false,
    ".write": false
  }
}

Change it to readable and writeable from everywhere for this tutorial only.

{
  /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
  "rules": {
    ".read": "auth === null",
    ".write": "auth === null"
  }
}

Click the publish button to update or save the changes. Now, the Google Firebase Realtime Database is ready to use with your Angular web app.


Step #2: Create a New Angular App

Before updating or installing the Angular CLI, make sure you have installed the latest Node.js and NPM. Type this command to check the version of Node.js and NPM.

node -v
v12.18.0
npm -v
6.14.5

Next, type this command to update or install the Angular CLI.

sudo npm install -g @angular/cli

Now, we have an @angular/cli version 9.1.7 in your terminal or Node environment. Next, create a new Angular app inside your Angular applications folder.

ng new angular-chat

For now, choose "y" to add Angular routing and leave other questions as default by pressing the Enter key. Next, go to the newly created Angular application, then open it with your Text Editor or IDE. To use VSCode, just type this command in the new Angular app folder.

cd ./angular-chat
code .

Now, the new Angular app is ready to develop.


Step #3: Install and Configure Firebase SDK

We will use the Google Firebase JavaScript SDK for accessing the Firebase Realtime Database. For that, type this command to install the module.

npm install --save firebase

Next, register the Firebase SDK module in the Angular Maps app by opening and editing this file `src/app/app.component.ts`, then add these imports of Firebase.

import * as firebase from 'firebase';

Declare a constant variable to hold the Firebase setting before `@Component` that contains the configuration variable for accessing Firebase using apiKey and databaseURL.

const config = {
  apiKey: 'YOUR_API_KEY',
  databaseURL: 'YOUR_DATABASE_URL'
};

You can find the API Key in the Firebase console under Settings(gear icon), and there is a Web API Key. For databaseURL, go to the Service Accounts tab under Settings, and you will see databaseURL in the Admin SDK configuration snippet for Node.js. Next, initialize Firebase configuration settings inside the Angular Component constructor.

constructor() {
  firebase.initializeApp(config);
}

Now, the Firebase Realtime Database is ready to use.


Step #4: Implementing Login

We will use a new component or page to implement a login form, which it's only a text field for the nickname and a login button. For that, add the new components for the required pages by typing these commands.

ng g component login
ng g component roomlist
ng g component addroom
ng g component chatroom

Next, we will register all of those components to the routing. Open and edit "src/app/app-routing.module.ts", then replace the routes constant with this.

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'roomlist/:nickname', component: RoomlistComponent },
  { path: 'addroom', component: AddroomComponent },
  { path: 'chatroom/:nickname/:roomid', component: ChatroomComponent },
  { path: '',
    redirectTo: '/login',
    pathMatch: 'full'
  }
];

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

<div class="container">
  <router-outlet></router-outlet>
</div>

Open and edit "src/app/app.component.css", then add these lines of CSS code.

.container {
  padding: 20px;
}

For the input form, we will use Angular Material and Reactive Forms. For that, add Angular Material to this project using schematics.

ng add @angular/material

If there are questions, just leave them as default by pressing the Enter key. Next, open and edit `src/app/app.module.ts`, then add these imports of the required Angular Material components and Angular ReactiveForm.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatCardModule } from '@angular/material/card';
import { MatTableModule } from '@angular/material/table';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSortModule } from '@angular/material/sort';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { DatePipe } from '@angular/common';

Add them into @NgModule imports.

  imports: [
    ...
    FormsModule,
    ReactiveFormsModule,
    MatInputModule,
    MatIconModule,
    MatCardModule,
    MatFormFieldModule,
    MatTableModule,
    MatProgressSpinnerModule,
    MatSortModule,
    MatSnackBarModule,
    MatSidenavModule
  ],

And add DatePipe to the @NgModule providers.

providers: [DatePipe],

Next, open and edit "src/app/login/login.component.ts", then add these lines of imports.

import { Router } from '@angular/router';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import * as firebase from 'firebase';

Add a class that implements the "ErrorStateMatcher" before the @Component.

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Declare all required variables at the top of the main class body.

  loginForm: FormGroup;
  nickname = '';
  ref = firebase.database().ref('users/');
  matcher = new MyErrorStateMatcher();

Inject the required Angular Router and FormBuilder modules into the constructor.

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

Add a condition to check if the nickname exists in the local storage and initialize the FormBuilder in the ngOnInit function.

  ngOnInit() {
    if (localStorage.getItem('nickname')) {
      this.router.navigate(['/roomlist']);
    }
    this.loginForm = this.formBuilder.group({
      'nickname' : [null, Validators.required]
    });
  }

Add a function to submit the login form to the Firebase Realtime Database.

  onFormSubmit(form: any) {
    const login = form;
    this.ref.orderByChild('nickname').equalTo(login.nickname).once('value', snapshot => {
      if (snapshot.exists()) {
        localStorage.setItem('nickname', login.nickname);
        this.router.navigate(['/roomlist']);
      } else {
        const newUser = firebase.database().ref('users/').push();
        newUser.set(login);
        localStorage.setItem('nickname', login.nickname);
        this.router.navigate(['/roomlist']);
      }
    });
  }

Next, open and edit "src/app/login/login.component.html", then replace all HTML tags with this Angular Material Form.

<div class="container">
  <form class="example-form" [formGroup]="loginForm" (ngSubmit)="onFormSubmit(loginForm.value)">
      <h2>Please login using your nickname</h2>
      <mat-form-field class="example-full-width">
        <mat-label>Nickname</mat-label>
        <input matInput placeholder="Enter your nickname" formControlName="nickname"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!loginForm.get('nickname').valid && loginForm.get('nickname').touched">Please enter your nickname</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
          <button type="submit" [disabled]="!loginForm.valid" mat-fab color="primary"><mat-icon>login</mat-icon></button>
      </div>
  </form>
</div>

Finally, adjust some style for this login form by adding these CSS codes to the "src/app/login/login.component.css".

.example-container {
  position: relative;
  padding: 5px;
}

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

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

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

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

.mat-flat-button {
  margin: 5px;
}


Step #5: Implementing Room List and Add Room

We will implement the Room list that contains a list of rooms, add a room button, and a logout button. For that, open and edit "src/app/roomlist/roomlist.component.ts", then add these imports of Angular ActivatedRoute, Router, Firebase, and the DatePipe.

import { ActivatedRoute, Router } from '@angular/router';
import * as firebase from 'firebase';
import { DatePipe } from '@angular/common';

We use DatePipe to convert the JavaScript Date to a string. Next, add this constant function before the @Component to extract or convert the Firebase response to an array of objects.

export const snapshotToArray = (snapshot: any) => {
  const returnArr = [];

  snapshot.forEach((childSnapshot: any) => {
      const item = childSnapshot.val();
      item.key = childSnapshot.key;
      returnArr.push(item);
  });

  return returnArr;
};

Declare the required variables at the top of the main class body.

  nickname = '';
  displayedColumns: string[] = ['roomname'];
  rooms = [];
  isLoadingResults = true;

Inject the imported modules and initialize the nickname from the local storage, and implement the request to the Firebase Realtime Database that always executes every time the Firebase document is changed.

  constructor(private route: ActivatedRoute, private router: Router, public datepipe: DatePipe) {
    this.nickname = localStorage.getItem('nickname');
    firebase.database().ref('rooms/').on('value', resp => {
      this.rooms = [];
      this.rooms = snapshotToArray(resp);
      this.isLoadingResults = false;
    });
  }

Add a function to enter the chat room that triggers when users choose the room in the template.

  enterChatRoom(roomname: string) {
    const chat = { roomname: '', nickname: '', message: '', date: '', type: '' };
    chat.roomname = roomname;
    chat.nickname = this.nickname;
    chat.date = this.datepipe.transform(new Date(), 'dd/MM/yyyy HH:mm:ss');
    chat.message = `${this.nickname} enter the room`;
    chat.type = 'join';
    const newMessage = firebase.database().ref('chats/').push();
    newMessage.set(chat);

    firebase.database().ref('roomusers/').orderByChild('roomname').equalTo(roomname).on('value', (resp: any) => {
      let roomuser = [];
      roomuser = snapshotToArray(resp);
      const user = roomuser.find(x => x.nickname === this.nickname);
      if (user !== undefined) {
        const userRef = firebase.database().ref('roomusers/' + user.key);
        userRef.update({status: 'online'});
      } else {
        const newroomuser = { roomname: '', nickname: '', status: '' };
        newroomuser.roomname = roomname;
        newroomuser.nickname = this.nickname;
        newroomuser.status = 'online';
        const newRoomUser = firebase.database().ref('roomusers/').push();
        newRoomUser.set(newroomuser);
      }
    });

    this.router.navigate(['/chatroom', roomname]);
  }

In that function, it will save a chat message that shows the user has entered the chat room and add new users to the "roomusers" document in the Firebase Realtime Database. Next, add a function to log out by removing the nickname from the local storage and redirecting back to the login page.

  logout(): void {
    localStorage.removeItem('nickname');
    this.router.navigate(['/login']);
  }

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

<div class="example-container mat-elevation-z8">
  <h3>{{nickname}} <button mat-flat-button (click)="logout()"><mat-icon>logout</mat-icon></button></h3>
  <h2>Room List</h2>
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <button mat-flat-button color="primary" [routerLink]="['/addroom']"><mat-icon>add</mat-icon></button>
  </div>
  <div class="mat-elevation-z8">
    <table mat-table [dataSource]="rooms" class="example-table"
           matSort matSortActive="roomname" matSortDisableClear matSortDirection="asc">

      <!-- Room Name Column -->
      <ng-container matColumnDef="roomname">
        <th mat-header-cell *matHeaderCellDef>Room Name</th>
        <td mat-cell *matCellDef="let row">{{row.roomname}}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="enterChatRoom(row.roomname)"></tr>
    </table>
  </div>
</div>

Give the room list styles by adding these lines of CSS code to the "src/app/roomlist/roomlist.component.css".

.example-container {
  position: relative;
  padding: 10px;
}

.example-table-container {
  position: relative;
  max-height: 400px;
  overflow: auto;
}

table {
  width: 100%;
}

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

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.example-rate-limit-reached {
  color: #980000;
  max-width: 360px;
  text-align: center;
}

/* Column Widths */
.mat-column-number,
.mat-column-state {
  max-width: 64px;
}

.mat-column-created {
  max-width: 124px;
}

To add a new room, we will use another component that will contain an Angular Material Form. Open and edit "src/app/addroom/addroom.component.ts", then add this line of imports.

import { Router, ActivatedRoute } from '@angular/router';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import * as firebase from 'firebase';

Add a class that implements the "ErrorStateMatcher" before the @Component.

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Declare all required variables at the top of the class body.

  roomForm: FormGroup;
  nickname = '';
  roomname = '';
  ref = firebase.database().ref('rooms/');
  matcher = new MyErrorStateMatcher();

Inject those imported modules to the constructor.

  constructor(private router: Router,
              private route: ActivatedRoute,
              private formBuilder: FormBuilder,
              private snackBar: MatSnackBar) {}

Initialize the FormBuilder in the ngOnInit function.

  ngOnInit(): void {
    this.roomForm = this.formBuilder.group({
      'roomname' : [null, Validators.required]
    });
  }

Add a function to submit the Angular form.

  onFormSubmit(form: any) {
    const room = form;
    this.ref.orderByChild('roomname').equalTo(room.roomname).once('value', (snapshot: any) => {
      if (snapshot.exists()) {
        this.snackBar.open('Room name already exist!');
      } else {
        const newRoom = firebase.database().ref('rooms/').push();
        newRoom.set(room);
        this.router.navigate(['/roomlist']);
      }
    });
  }

Next, implement the form template by opening and editing "src/app/addroom/addroom.component.html" then replace all HTML tags with this.

<div class="container">
  <form class="example-form" [formGroup]="roomForm" (ngSubmit)="onFormSubmit(roomForm.value)">
      <h2>Please enter new Room</h2>
      <mat-form-field class="example-full-width">
        <mat-label>Room Name</mat-label>
        <input matInput placeholder="Enter room name" formControlName="roomname"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!roomForm.get('roomname').valid && roomForm.get('roomname').touched">Enter room name</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
          <button type="submit" [disabled]="!roomForm.valid" mat-fab color="primary"><mat-icon>save</mat-icon></button>
      </div>
  </form>
</div>

Finally, give that form styles by adding these CSS codes to the "src/app/addroom/addroom.component.css".

.example-container {
  position: relative;
  padding: 5px;
}

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

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

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

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

.mat-flat-button {
  margin: 5px;
}


Step #6: Implementing Chat Room

Now, we will implement the main steps of this Angular tutorial, which is the Chat room, which has the online users list, the chatbox, and a message form. We split the user list and the chatbox by using Angular Material Navigation Drawer (mat-drawer). Open and edit "src/app/chatroom/chatroom.component.ts", then add or modify these required imports.

import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import * as firebase from 'firebase';
import { DatePipe } from '@angular/common';

Add a class that implements the "ErrorStateMatcher" required by the chat message from before the @Component.

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Add this constant function before the @Component to extract or convert the Firebase response to an array of objects.

export const snapshotToArray = (snapshot: any) => {
  const returnArr = [];

  snapshot.forEach((childSnapshot: any) => {
      const item = childSnapshot.val();
      item.key = childSnapshot.key;
      returnArr.push(item);
  });

  return returnArr;
};

Add these variables to implement an auto-scroll to the chat box at the top of the main class body.

  @ViewChild('chatcontent') chatcontent: ElementRef;
  scrolltop: number = null;

Also, add these required variables of the chat list, user list, and message form.

  chatForm: FormGroup;
  nickname = '';
  roomname = '';
  message = '';
  users = [];
  chats = [];
  matcher = new MyErrorStateMatcher();

Inject the required imported modules into the constructor and get the nickname from the local storage, the room name from the router params, and get the users and chats list from the Firebase Realtime Database documents.

  constructor(private router: Router,
              private route: ActivatedRoute,
              private formBuilder: FormBuilder,
              public datepipe: DatePipe) {
                this.nickname = localStorage.getItem('nickname');
                this.roomname = this.route.snapshot.params.roomname;
                firebase.database().ref('chats/').on('value', resp => {
                  this.chats = [];
                  this.chats = snapshotToArray(resp);
                  setTimeout(() => this.scrolltop = this.chatcontent.nativeElement.scrollHeight, 500);
                });
                firebase.database().ref('roomusers/').orderByChild('roomname').equalTo(this.roomname).on('value', (resp2: any) => {
                  const roomusers = snapshotToArray(resp2);
                  this.users = roomusers.filter(x => x.status === 'online');
                });
              }

Initialize the Form group for the message-form inside the ngOnInit function.

  ngOnInit(): void {
    this.chatForm = this.formBuilder.group({
      'message' : [null, Validators.required]
    });
  }

Add a function to submit the message form and save it to the Firebase Realtime Database document.

  onFormSubmit(form: any) {
    const chat = form;
    chat.roomname = this.roomname;
    chat.nickname = this.nickname;
    chat.date = this.datepipe.transform(new Date(), 'dd/MM/yyyy HH:mm:ss');
    chat.type = 'message';
    const newMessage = firebase.database().ref('chats/').push();
    newMessage.set(chat);
    this.chatForm = this.formBuilder.group({
      'message' : [null, Validators.required]
    });
  }

Add a function to exit the chat room, which is to send the exit message to the Firebase Realtime Database, set the room user status, and go back to the room list.

  exitChat() {
    const chat = { roomname: '', nickname: '', message: '', date: '', type: '' };
    chat.roomname = this.roomname;
    chat.nickname = this.nickname;
    chat.date = this.datepipe.transform(new Date(), 'dd/MM/yyyy HH:mm:ss');
    chat.message = `${this.nickname} leave the room`;
    chat.type = 'exit';
    const newMessage = firebase.database().ref('chats/').push();
    newMessage.set(chat);

    firebase.database().ref('roomusers/').orderByChild('roomname').equalTo(this.roomname).on('value', (resp: any) => {
      let roomuser = [];
      roomuser = snapshotToArray(resp);
      const user = roomuser.find(x => x.nickname === this.nickname);
      if (user !== undefined) {
        const userRef = firebase.database().ref('roomusers/' + user.key);
        userRef.update({status: 'offline'});
      }
    });

    this.router.navigate(['/roomlist']);
  }

Next, implement this chat room in the template that contains the user list, chatbox, and message form. Open and edit "src/app/chatroom/chatroom.component.html", then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <mat-drawer-container class="drawer-container">
    <mat-drawer mode="side" opened class="left-drawer">
      <div class="users-pane">
        <mat-card class="users-card">
          <button type="button" mat-button matSuffix mat-icon-button aria-label="Exit" (click)="exitChat()">
            <mat-icon>logout</mat-icon>
          </button>
        </mat-card>
        <mat-card class="users-card" *ngFor="let user of users">
          <mat-icon>person</mat-icon> <span class="username">{{user.nickname}}</span>
        </mat-card>
      </div>
    </mat-drawer>
    <mat-drawer-content class="chat-pane">
      <div #chatcontent [scrollTop]="scrolltop" class="chat-content">
        <div class="message-box" *ngFor="let chat of chats">
          <div class="chat-status" text-center *ngIf="chat.type==='join'||chat.type==='exit';else message">
            <span class="chat-date">{{chat.date | date:'short'}}</span>
            <span class="chat-content-center">{{chat.message}}</span>
          </div>
          <ng-template #message>
            <div class="chat-message">
              <div class="right-bubble" [ngClass]="{'right-bubble': chat.nickname === nickname, 'left-bubble': chat.nickname !== nickname}">
                <span class="msg-name" *ngIf="chat.nickname === nickname">Me</span>
                <span class="msg-name" *ngIf="chat.nickname !== nickname">{{chat.nickname}}</span>
                <span class="msg-date"> at {{chat.date | date:'short'}}</span>
                <p text-wrap>{{chat.message}}</p>
              </div>
            </div>
          </ng-template>
        </div>
      </div>
      <footer class="sticky-footer">
        <form class="message-form" [formGroup]="chatForm" (ngSubmit)="onFormSubmit(chatForm.value)">
          <mat-form-field class="message-form-field">
            <input matInput placeholder="Enter message here" formControlName="message"
                    [errorStateMatcher]="matcher">
            <mat-error>
              <span *ngIf="!chatForm.get('message').valid && chatForm.get('message').touched">Enter your message</span>
            </mat-error>
            <button type="submit" [disabled]="!chatForm.valid" mat-button matSuffix mat-icon-button aria-label="Send">
              <mat-icon>send</mat-icon>
            </button>
          </mat-form-field>
        </form>
      </footer>
    </mat-drawer-content>
  </mat-drawer-container>
</div>

Finally, give that template style by adding these lines of CSS code to the "src/app/chatroom/chatroom.component.css".

.example-container {
  display: flex;
  padding: 10px;
}

.drawer-container {
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  top: 0;
}

.left-drawer {
  width: 200px;
}

.users-pane {
  height: 500px;
}

.users-card {
  margin: 5px 20px;
}

.username {
  position: absolute;
  margin-top: 5px;
  margin-left: 10px;
}

footer.sticky-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 10px;
  background-color: #ffffff;
  border-top: solid 1px #efefef;
}

.message-form {
  margin-left: 200px;
}

.message-form-field {
  width: 94%;
  margin: 0 4% 0 2%;
}

.message-box {
  float: left;
  width: 98%;
  margin: 5px 0 0 2%;
}

.chat-message {
  width: 80%;
  min-height: 40px;
}

.chat-message .right-bubble {
  position: relative;
  background: #dcf8c6;
  border-top-left-radius: .4em;
  border-bottom-left-radius: .4em;
  border-bottom-right-radius: .4em;
  padding: 5px 10px 10px;
  left: 15%;
}

.chat-message .right-bubble span.msg-name {
  font-size: 12px;
  font-weight: bold;
  color: green;
}
.chat-message .right-bubble span.msg-date {
  font-size: 10px;
}

.chat-message .right-bubble:after {
  content: '';
  position: absolute;
  right: 0;
  top: 13px;
  width: 0;
  height: 0;
  border: 27px solid transparent;
  border-left-color: #dcf8c6;
  border-right: 0;
  border-top: 0;
  margin-top: -13.5px;
  margin-right: -27px;
}
.chat-message .left-bubble {
  position: relative;
  background: lightblue;
  border-top-right-radius: .4em;
  border-bottom-left-radius: .4em;
  border-bottom-right-radius: .4em;
  padding: 5px 10px 10px;
  left: 5%;
}
.chat-message .left-bubble span.msg-name {
  font-size: 12px;
  font-weight: bold;
  color: blue;
}
.chat-message .left-bubble span.msg-date {
  font-size: 10px;
}
.chat-message .left-bubble:after {
  content: '';
  position: absolute;
  left: 0;
  top: 13px;
  width: 0;
  height: 0;
  border: 27px solid transparent;
  border-right-color: lightblue;
  border-left: 0;
  border-top: 0;
  margin-top: -13.5px;
  margin-left: -27px;
}

.chat-message .chat-status {
  min-height: 49px;
}

.chat-message .chat-status .chat-date {
  display: block;
  font-size: 10px;
  font-style: italic;
  color: #fff;
  text-shadow: 0px -1px 0px #222, 0px 1px 0px #aaa;
  height: 15px;
  left: 10%;
  right:10%;
}

.chat-message .chat-status .chat-content-center {
  padding: 5px 10px;
  background-color: #e1e1f7;
  border-radius: 6px;
  font-size: 12px;
  color: #555;
  height: 34px;
  left: 10%;
  right:10%;
}

.chat-content {
  overflow-y: scroll;
  height: 600px;
}


Step #7: Run and Test Angular Firebase Chat Web App

We will use a separate web browser to test this Angular Firebase Chat Web App. Next, run the Angular app by typing this command.

ng serve --open

The default browser will open this application automatically. Next, open another web browser, then go to "localhost:4200" and you will see this whole Angular Firebase app like this.

Angular 9 Tutorial: Creating Firebase Chat Web App - demo 1
Angular 9 Tutorial: Creating Firebase Chat Web App - demo 2
Angular 9 Tutorial: Creating Firebase Chat Web App - demo 3
Angular 9 Tutorial: Creating Firebase Chat Web App - demo 4
Angular 9 Tutorial: Creating Firebase Chat Web App - demo 6

And you will see this Firebase real-time database structure like this in the Firebase console.

Angular 9 Tutorial: Creating Firebase Chat Web App - db structure

That's the Angular Tutorial: Creating a Firebase Chat Web App. You can get the working source code from our GitHub.

If you don’t want to waste your time designing your own front-end or your budget to spend by hiring a web designer, then Angular Templates is the best place to go. So, speed up your front-end web development with premium Angular templates. Choose your template for your front-end project here.

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

Thanks!