• Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
No Result
View All Result

Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io – Part 3

Connecting Angular application to Socket Server and Implementing Real-time chat list

by Shashank Tiwari
October 20, 2019
in Angular
5
10 Minutes Read
Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io part3

This is the third part of the Building Real time private chatting app using Angular. In this part, you will implement below-listed features in your application,

  1. You will connect your Angular application to Socket Server.
  2. You will implement the Realtime chat list for the application.
  3. You will write Socket Server Event to Emit the updated Chat List.
  4. Highlight the select user from the list of users

Before Going any further a little recap,
In the previous part of the series, we completed the Login/Registration process in our application. After Login/Registration, the application will redirect the user to a home page of the application. Once you redirected to the homepage, the application will check the session of the user by making an HTTP request.

As soon as the application finishes the session checking, we will connect this Angular to the Nodejs socket server. Once socket server is connected, just after that we will subscribe to real-time chat list update.

So let’s get our hands dirty and start integrating real-time goodness into our application.

 Download

 Demo

 




 

Connecting Angular application to Socket Server

We won’t just connect the Socket server by using below code, I know in most of the article you have seen, read and wrote this code to connect the Socket Server.

const socket = io('localhost:3000');

Here we will add a little extra sugar by passing a parameter while connecting to the socket server.

=>Which we will do by using the code shown below,

this.socket = io(this.BASE_URL, { query: `userId=${userId}` });

Now you may ask Why we need to this in the first place?
Well, the answer is simple. We need userId to identify the which user is just connected to socket Server. Also, we will update the socket id of the connected user in MongoDB.

=> The updated socketId will be used at the different stages of the application.

=>Since you have everything ready to go, all you have to do is call connectSocket()(This method is defined inside the socket.service.ts file) from HomeComponent class. Open the home.component.ts file and add the below inHomeComponentclass,

home.component.ts:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/ngOnInit() {
    this.userId = this.dataShareService.getUserId();
    this.username = this.dataShareService.getUserName();
    this.establishSocketConnection();
}

async establishSocketConnection() {
    try {
        if (this.userId === '' || typeof this.userId === 'undefined' || this.userId === null) {
            this.router.navigate(['/']);
        } else {
            /* making socket connection by passing UserId. */            await this.socketService.connectSocket(this.userId);
            this.overlayDisplay = false;
        }
    } catch (error) {
        alert('Something went wrong');
    }
}

As I said earlier, we will update the socketId of the user in the MongoDB database, hence we can use it later in the application. If you check the socket connection string in network tab, you should see something similar to the below-shown image.

As of now, I am using the Chrome browser. Though it has nothing to do with what kind of browser you use. Anyways make sure you are passing userid while connecting socket server.

Passing user id in socket connection

 

Now you have provided a userId as a parameter in socket connection. Let’s complete abstract work in server side.

Open the socket.js file and add the below code to it,

socket.js:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/socketConfig(){
    this.io.use( async (socket, next) => {
        try {
            await queryHandler.addSocketId({
                userId: socket.request._query['userId'],
                socketId: socket.id
            });
            next();
        } catch (error) {
            // Error
            console.error(error);
        }
      });

    this.socketEvents();
}

Explanation:

  • The first thing to notice here we are reading the userId parameter by using line shown below,
    socket.request._query['userId']
  • Second, we haveaddSocketId() method to update the data in MongoDB.
  • And at the end, we have called rest of the socket server Event by callingthis.socketEvents() method.

Implementing a Realtime chat List

I have divided this section into the smaller sections, since covering everything into a single section would be very nosogenic to read and understand. Creating chat list involves below-listed points,

  1. Creating ChatList component.
  2. Writing Markup for a chat list
  3. WritingChatList component Class.
  4. Writing Socket server Event.
  5. Updating user’s online status in ChatList
  6. Selecting a User from the chat list of the user.

By the end of this article, you will have full full-fledged working chat list for your application. So let’s dig our teeth into it and finish it.

Creating ChatList component

Why a new component for ChatList? Though we can write all the markup and Code inside the HomComponent. But, It’s not a good practice and your HomeComponent class will be heavy and long to manage.

=> Therefore first things first, you have to createChatListcomponent nothing so special about it. Execute the below Angular CLI commands.

=> The First command to create ChatList Module,

ng g module ChatList

=> Then the second Will generate the Actual Component,

ng g component ChatList

=> Now yourChatListmodule is ready to use, So let’s import theChatListmodule intoHomeModule. Open the home.module.ts and add the below line and make your home.module.ts file look like this,

home.module.ts:

/* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { UISupportModule } from './../../modules/ui-support/ui-support.module';

import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
import { ConversationModule } from './conversation/conversation.module';
import { ChatListModule } from './chat-list/chat-list.module';

@NgModule({
    imports: [
        CommonModule,
        HomeRoutingModule,
        UISupportModule,
        ConversationModule,
        ChatListModule
    ],
    declarations: [HomeComponent]
})
export class HomeModule { }

Now you add the<app-chat-list> into your home.component.html file, and it should show the traditional chat-list works! message.

So at this point, your home.component.html file should look like this.

home.component.html:

<!-- 
    Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io 
    @author Shashank Tiwari
-->

<!-- Loading overlay section starts -->
<div *ngIf="overlayDisplay" class="overlay">
    <h1>Loading</h1>
</div>
<!-- Loading overlay section starts -->

<!-- header section starts -->
<header class="app-header">
    <nav class="navbar navbar-expand-md">
        <a class="navbar-brand" href="#">Hello {{username}}</a>
    </nav>
    <ul class="nav justify-content-end">
        <li class="nav-item">
            <a class="nav-link active" href="#" (click)="logout()">Logout</a>
        </li>
    </ul>
</header>
<!-- header section ends -->

<main role="main" class="container content"  *ngIf="!overlayDisplay">
    <div class="row chat-content">
        <div class="col-3 chat-list-container">
            <app-chat-list></app-chat-list>
        </div>
        <div class="col-8 message-container">
            <!-- Component to display the conversation -->
        </div>
    </div>
</main>

Writing Markup for a chat list

Open thechat-list.component.html and add the below-shown markup. The below code is ridiculously easy to understand.

=> The next thing ischatListUsersproperty of ChatListComponent class.

=> InchatListUsersproperty we will store the list of the users. This variable is nothing but an array, which updates itself on certain stipulated conditions.

chat-list.component.html:

<!-- 
    Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
    @author Shashank Tiwari
 -->
<ul class="user-list">
  <li *ngFor="let user of chatListUsers">
    {{ user.username}}
  </li>
</ul>
<div class="alert" alert-info.class="!loading" *ngIf='chatListUsers.length === 0'>
  {{ loading && chatListUsers.length === 0 ? 'Loading your chat list.' : 'No User Avilable to chat.'}}
</div>

Writing ChatList component Class

At this point, your ChatList component will render nothing. So to make it work we will add some code to the ChatList component class. Basically, in this class, do two important things.

=>First we will update the chat list and the second, we will pass the data of the selected user from the list of users to the Conversation component.

=>But first, let’s add the necessary properties required in ChatList component class, open the chat-list.component.ts and add the below properties,

chat-list.component.ts:

loading = true;
userId: string = null;
chatListUsers: User[] = [];

Now let’s add the code to update the chat list, this code will basically manipulate the chatListUsers property.

So open the chat-list.component.ts and add the below code.

chat-list.component.ts:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/
ngOnInit() {
    this.loading = true;
    this.userId = this.dataShareService.getUserId();
    this.socketService.getChatList(this.userId).subscribe((chatListResponse: ChatListResponse) => {
        this.renderChatList(chatListResponse);
    });
}

renderChatList(chatListResponse: ChatListResponse): void {
    if (!chatListResponse.error) {
        if (chatListResponse.singleUser) {
            if (this.chatListUsers.length > 0) {
                this.chatListUsers = this.chatListUsers.filter(function (obj: User) {
                    return obj.id !== chatListResponse.chatList[0].id;
                });
            }
            /* Adding new online user into chat list array */            this.chatListUsers = this.chatListUsers.concat(chatListResponse.chatList);
        } else if (chatListResponse.userDisconnected) {
            const loggedOutUser = this.chatListUsers.findIndex((obj: User) => obj.id === chatListResponse.userid);
            if (loggedOutUser >= 0) {
                this.chatListUsers[loggedOutUser].online = 'N';
            }
        } else {
            /* Updating entire chatlist if user logs in. */            this.chatListUsers = chatListResponse.chatList;
        }
        this.loading = false;
    } else {
        alert(`Unable to load Chat list, Redirecting to Login.`);
        this.chatService.removeLS()
            .then(async (removedLs: boolean) => {
                await this.router.navigate(['/']);
                this.loading = false;
            })
            .catch(async (error: Error) => {
                alert(' This App is Broken, we are working on it. try after some time.');
                await this.router.navigate(['/']);
                console.warn(error);
                this.loading = false;
            });
    }
}

We know what the above code does, let’s understand how the above code works!

Explanation :

  1. UsinggetChatList()method we are updating the list of the user, which is defined into the SocketService service.
  2. For example, If a new user logs in into the system or an existing user goes offline in both the cases we will update the list of online users.
  3. Here I am emitting the complete list of online/offline users from the nodejs socket server to the user who justlogged into the system.
  4. And, for the rest of users, those who are already online will get the users information whowent offlineorjust logged in. This makes sense, right? Because sending the complete list of online users to each and every user is not good.
  5. Now if you see the above code there iselse if()code block,for the below task.
  6. If the response from the socket event contains thesingleUser propertywhich means the new socket connection is established.
  7. If the response contains theuserDisconnected propertythen it means the user went and offline remove the user from the chat list.
  8. And at the end, In the else condition we will update the list of users in the else block.

Writing Socket Server Events to Emit the updated Chat List

All the above only happen if you have Socket server and which responds to the updated chat list. Anyways, let’s go ahead and talk something constructive.

=> Here you will write socket.io.onlistener on the server side when a client requests it.

=> Once Angular asks for chat list to Socker server, the socket will query the details from the database.

=>After querying, based on the user it emits the result.

So this was the basic idea, let’s see the code to implement the same. Open the socket.js and add the below code,

socket.js:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/socket.on(`chat-list`, async (data) => {
    if (data.userId == '') {
        this.io.emit(`chat-list-response`, {
            error : true,
            message : CONSTANTS.USER_NOT_FOUND
        });
    }else{
        try {
            const [UserInfoResponse, chatlistResponse] = await Promise.all([
                    queryHandler.getUserInfo( {
                        userId: data.userId,
                        socketId: false
                    }),
                    queryHandler.getChatList( socket.id )
                ]);
            this.io.to(socket.id).emit(`chat-list-response`, {
                error : false,
                singleUser : false,
                chatList : chatlistResponse
            });
            socket.broadcast.emit(`chat-list-response`,{
                error : false,
                singleUser : true,
                chatList : UserInfoResponse
            });
        } catch ( error ) {
            this.io.to(socket.id).emit(`chat-list-response`,{
                error : true ,
                chatList : []
            });
        }
    }
});

Explanation:

The above code seems easy to understand. But me being me, am gonna explain it anyway.

  1. First thing first, we will check for the validation. If all goes well, process ahead else emit a response with an error message.
  2. Second, inside the try-catch block, we will execute all the things. Why ? because you never know when your code will explode.
  3. Just kidding,try-catch is there to handle the unknown exceptions and unhandled promise rejections. And I feel like it’s a good practice to follow.
  4. Then inside the try-catch block, you will first fetch the information about the user by calling getUserInfo(). Later you all thegetChatList() which is defined inside the QueryHandler class.
  5. Why are you emitting two socket Events?As I explained in the above section, you don’t wanna send the entire chat list to the user instead send only information about the user who goes online/offline.

Let’s take a look at those method defined in theQueryHandlerclass. Open the query-handler.js and add the below code,

In the below code, We are just executing a MongoDB query. Based on the socketId parameter, we are projecting the result from the database.

query-handler.js:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/getUserInfo({userId,socketId = false}){
    let queryProjection = null;
    if(socketId){
        queryProjection = {
            "socketId" : true
        }
    } else {
        queryProjection = {
            "username" : true,
            "online" : true,
            '_id': false,
            'id': '$_id'
        }
    }
    return new Promise( async (resolve, reject) => {
        try {
            const [DB,ObjectID] = await this.Mongodb.onConnect();
            DB.collection('users').aggregate([{
                $match:  {
                    _id : ObjectID(userId)
                }
            },{
                $project : queryProjection
            }
            ]).toArray( (err, result) => {
                DB.close();
                if( err ){
                    reject(err);
                }
                socketId ? resolve(result[0]['socketId']) : resolve(result);
            });
        } catch (error) {
            reject(error)
        }
    });
}

Now the last method left is to fetch the chat list from the database. Honestly Speaking you are just fetching all the user from the database and displaying it to the front end based on the online/offline status.

So don’t get surprised you if you see me querying all the user from the database. in case if you want to build a really custom chat list, then I say this again, Download the above E-book.

Open the query-handler.js and add the below code. In the below code, we will fetch all the records from the database.

query-handler.js:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/
getChatList(userId){
    return new Promise( async (resolve, reject) => {
        try {
            const [DB,ObjectID] = await this.Mongodb.onConnect();
            DB.collection('users').aggregate([{
                $match: {
                    'socketId': { $ne : userId}
                }
            },{
                $project:{
                    "username" : true,
                    "online" : true,
                    '_id': false,
                    'id': '$_id'
                }
            }
            ]).toArray( (err, result) => {
                DB.close();
                if( err ){
                    reject(err);
                }
                resolve(result);
            });
        } catch (error) {
            reject(error)
        }
    });
}

In the above code, we are executing the MongoDB query. This query will fetch the list of users from the database by using the given condition. After executing the query, it will return the result trough Promise.

Once you implement all this you should be properly working chat list as shown in below image,

Real time chat list in private chatting app using Angular 2, Nodejs, mongodb and Socket.io

Realtime Chat list

Selecting a user from the chat list to chat

Till now you have successfully implemented the chat list feature in your application. Let’s go one more step ahead and add the feature to highlight the user selected for the chatting.

=>In this section we will just highlight the selected user from the chat list.

=> Open the chat-list.componet.html and replace the code, Here we are using bootstrap classes, so you don’t need to write any CSS for that.

chat-list.componet.html:

<!-- 
    Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
    @author Shashank Tiwari
 -->

<ul class="user-list">
    <li *ngFor="let user of chatListUsers" 
        (click)="selectedUser(user)" 
        [class.active]="isUserSelected(user.id)"
    >
        {{ user.username}}
        <span [ngClass]="user.online === 'Y' ? 'online' : 'offline'"> </span>
    </li>
</ul>
<div class="alert" alert-info.class="!loading" *ngIf='chatListUsers.length === 0'>
    {{ loading && chatListUsers.length === 0 ? 'Loading your chat list.' : 'No User Avilable to chat.'}}
</div>

Explanation:

  1. As you can see the method isUserSelected() will return true or false, that’s the only motive of that method.
  2. It will compare the user selected userId with the user id that you are passing insideisUserSelected() method.

I won’t explain the entire working of this, but I have written a separate post of the same. Make sure you read, how to Highlight selected row in Angular 2, that will help you to understand this.

=> Open the chat-list.compoenet.ts and add the below property in the ChatListComponent class,

private selectedUserId: string = null;

This property will hold the value of selected user id. To complete this feature, add the below methods in thechat-list.compoenet.ts.

chat-list.compoenet.ts:

/*
* Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/
isUserSelected(userId: string): boolean {
    if (!this.selectedUserId) {
        return false;
    }
    return this.selectedUserId === userId ? true : false;
}

selectedUser(user: User): void {
    this.selectedUserId = user.id;
    this.dataShareService.changeSelectedUser(user);
}

Explanation:

  1. isUserSelected() method will return true or false based on a comparison of selectedUserId and userId.
  2. selectedUser() method will update the selectedUserId property. Also, this method will emit the latest selected user id using behavior subject.

Finishing Note

I believe this part of the series was short as compare to others. Let’s recall one more time, what we implemented here before wrapping this part. We implemented below-listed features in our application,

  1. We connected our Angular application to Socket Server.
  2. We implemented the Realtime chat list for the application.
  3. We wrote Socket Server Event to Emit the updated Chat List.
  4. Highlighted the select user from the list of users

For now, that’s it. I’ll see you in next and the last of the series where will implement real-time chat feature.

 

 

 

 

Tags: AngularAngular chatAngular socket.ioChatMongoDBNodejs
Previous Post

Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io – Part 2

Next Post

Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io – Part 4

Related Posts

How to use Web Share API in your Angular applications
Angular

How to use Web Share API in your Angular applications

August 8, 2018
Implementing Swipe to delete in Angular
Angular

Implementing Swipe to delete in Angular

August 8, 2018
Building a 2D racing Game using Angular
Angular

Building a 2D racing Game using Angular

August 8, 2018
Next Post
Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io part 4

Real time private chatting app using Angular 2, Nodejs, mongodb and Socket.io – Part 4

Comments 5

  1. moore moore says:
    8 years ago

    Are you taking on any custom work with this app ?

    Reply
    • Shashank says:
      8 years ago

      Replied you on your email.

      Reply
  2. moral mirila says:
    8 years ago

    can u explain where where exactly i need to put this code
    “this.router.navigate([‘/home/’+result.userId]);”
    at app.routing.ts thanks alot

    Reply
    • Shashank says:
      7 years ago

      Thank you 🙂

      Reply
  3. Mahesh says:
    7 years ago

    socket.request._query[‘userId’] is give undefined object

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *




https://codershood.info

www.codershood.info programming blog dedicated to providing high-quality coding tutorial and articles on web development, Angular, React, Laravel, AngularJs, CSS, Node.js, ExpressJs and many more. We also provide ebook based on complicated web application along with the source code.

  • Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.

No Result
View All Result
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.