Angular ChatApp using  FireStore

Manav Goel

Software Engineer at 

GeekyAnts

  • Observable based - Use the power of RxJS, Angular, and Firebase.
  • Realtime bindings - Synchronizes data in realtime.
  • Authentication - Log users in with a variety of providers and monitor authentication state in realtime.
  • Offline Data - Store data offline automatically with AngularFirestore.
@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(firebaseConfig)
    AngularFirestoreModule.enablePersistence() /// <--- update this line
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }
  • ngrx friendly - Integrate with ngrx using AngularFire's action based APIs.

         Introduction to Angular FireStore (AngularFire)

Firebase offers two cloud-based, client-accessible database solutions that support realtime data syncing:
 

  • Realtime Database is Firebase's original database. It's an efficient, low-latency solution for mobile apps that require synced states across clients in realtime.

     
  • Cloud Firestore is Firebase's new flagship database for mobile app development. It improves on the successes of the Realtime Database with a new, more intuitive data model. Cloud Firestore also features richer, faster queries and scales better than the Realtime Database.

                 Database? : Cloud Firestore or Realtime Database

Cloud Firestore vs Realtime Database

(Data Model)

Realtime Database-:

  • Stores data as one large JSON tree.
  • Complex, hierarchical data is harder to organize at scale.

Cloud Firestore -:

  • Stores data in documents organized in collections.
  • Complex, hierarchical data is easier to organize at scale, using sub-collections within documents.

Cloud Firestore vs Realtime Database

(Data Model)

 

npm install firebase angularfire2 --save

Copy the above command in the terminal, Hit enter and you are ready to go.....

How to install Angularfire

Step 1: Create a new database project

Setting Up Firebase

Step 2: Database dashboard.

Setting Up Firebase (Contd.)

Step 3: Config.

Setting Up Firebase (Contd.)

Step 4: Add FireStore configs to environment variable
(/src/environments/environment.ts).

Setting Up Firebase (Contd.)

export const environment = {
  production: false,
  firebase: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};

Step 5:  Setup @NgModule for the AngularFireModule.

Setting Up Firebase (Contd.)

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from 'angularfire2';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase)
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

Step 6:  Setup individual @NgModules

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from 'angularfire2';
import { AngularFirestoreModule } from 'angularfire2/firestore';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase, 'my-app-name'), // imports firebase/app needed for everything
    AngularFirestoreModule, // imports firebase/firestore, only needed for database features
    AngularFireAuthModule, // imports firebase/auth, only needed for auth features
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

For example if your application was using both Firebase authentication and the Firebase database you would add:

Setting Up Firebase (Contd.)

Step 7:  Inject AngularFirestore.

Setting Up Firebase (Contd.)

import { Component } from '@angular/core';
import { AngularFirestore } from 'angularfire2/firestore';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  constructor(db: AngularFirestore) {

  }
}

Step 8:  Bind to a list.

import { Component } from '@angular/core';
import { AngularFirestore } from 'angularfire2/firestore';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  items: Observable<any[]>;
  constructor(db: AngularFirestore) {
    this.items = db.collection('items').valueChanges();
  }
}
<ul>
  <li class="text" *ngFor="let item of items | async">
    {{item.name}}
  </li>
</ul>

/src/app/app.component.ts

/src/app/app.component.html

Setting Up Firebase (Contd.)

Chat App

Get Data with Cloud Firestore

There are two ways to retrieve data stored in Cloud Firestore -:

  1. Call a method to get the data
this.db.collection("users")
    .doc(this.currentUserID)
    .get()
    .then((data)=> {
        this.currentUser = doc.data() as User;
    }).catch(function(error) {
        console.error("Error removing document from users: ", error);
    });
constructor(private db: AngularFirestore) {}
interface User{
    user_name: string;
    joinedAt: Date;
}

Get Data with Cloud Firestore

2. Set a listener to receive data-change events.

2.1. Use valueChanges()

this.messageList = this.msgCollection.valueChanges();
this.users = this.usersCollection.valueChanges();
  • valueChanges() returns current state of the collection.
  • Returns an Observable of data as a synchronized array of JSON objects.
  • All Snapshot metadata is stripped.
msgCollection: AngularFirestoreCollection<Message>;
messageList: Observable<Message[]>;
users: Observable<User[]>;
Interface Message{
    msg: string;
    createdAt: Date;
    id?: string;
}

Get Data with Cloud Firestore

this.db.collection("users")
	.snapshotChanges()
	.map(arr=>{
	    this.usersArrObj = {};
	    arr.map((ab)=>{
                const data = ab.payload.doc.data() as User;
                const id = ab.payload.doc.id;
                this.usersArrObj[id] = {...data}.user_name;
	    });
        }).subscribe();

2. Set a listener to receive data-change events.

2.2. Use snapshotChanges()

  • Also returns current state of the collection. Returns an Observable of data as a synchronized array of DocumentChangeAction[]
  • Data along with the metadata
  • Metadata provides you the underyling DocumentReference, document id, and array index of the single document

Get Data with Cloud Firestore

Output of snapshotChanges()

Add Data In Cloud Firestore

this.db.collection("messages").add({
        msg: data,
        createdAt: new Date(),
        userID: this.currentUserID,
        userName: this.currentUser
    })
    .then((docRef)=> {
        this.messageBox.nativeElement.value = "";
    })
    .catch(function(error) {
        console.error("Error adding document: ", error);
    });

There are several ways to write data to Cloud Firestore:

  • Set the data of a document within a collection, explicitly specifying a document identifier.
  • Add a new document to a collection. In this case, Cloud Firestore automatically generates the document identifier.
  • Create an empty document with an automatically generated identifier, and assign data to it later.

Delete Data From Cloud Firestore

Using delete() method:

this.db.collection("users")
    .doc(this.currentUserID)
    .delete()
    .then((data)=> {
        this.userService.setUserID('');
    }).catch(function(error) {
        console.error("Error removing document from users: ", error);
    });

Realtime Updates in Cloud Firestore

Using stateChanges() method:

  • Returns an Observable of the most recent changes as a DocumentChangeAction[].
  • It emits changes as they occur rather than syncing the query order.
this.usersCollection.stateChanges(['added'])
    .map(actions => {
	if(actions.length === 1){
            actions
                .map(a => {
                    this.newUser = true;
                    const data = a.payload.doc.data() as User;
                    this.newlyAddedUser = data.user_name.toString();
                    const id = a.payload.doc.id;
                    return { id, ...data };
                })
        }
    }).subscribe();

Querying Collections in Cloud Firestore

Firestore Collection ≈ RealtimeDB List

Collections are just containers of documents.

They have their own query methods that are way more developer-friendly than the realtimeDB.

Here are some of the cool things you can do that were previously difficult -:

this.msgCollection = this.db.collection('messages', ref=>{
    return ref
        .orderBy('createdAt')
        .where('createdAt', '>=' , this.currentUserDate? this.currentUserDate : new Date());
    });

Important Links

Cloud Firestore Docs ->

https://firebase.google.com/docs/firestore/

THANK YOU!

@manav1020goel

ChatApp using Angular and Firestore

By Manav Goel

ChatApp using Angular and Firestore

  • 3,510