0

Description:

I am currently developing a chat application using node.js and socket.io... What my problem is ... when ever user X send a message to user Y or vice versa i am storing in mongo DB ... so i want to show the count of message received from Y to User X and count of messages received from X to Y as below shown image.

Problem: If i do console.log it is working but if i am appending the count of messages to html then it is showing last occurrence only

Here is the screen shot which contains two online users with last occurrence messages count..

Below is the client side Java script code.

READ THE COMMENTS IN THE BELOW CODE TO UNDERSTAND

    $(function() {

    var socket = io('/chat');

    var username = $('#user').val();
    var noChat = 0; //setting 0 if all chats histroy is not loaded. 1 if all chats loaded.
    var msgCount = 0; //counting total number of messages displayed.
    var oldInitDone = 0; //it is 0 when old-chats-init is not executed and 1 if executed.
    var roomId; //variable for setting room.
    var toUser;

    //passing data on connection.
    socket.on('connect', function() {
        socket.emit('set-user-data', username);
        // setTimeout(function() { alert(username+" logged In"); }, 500);

        socket.on('broadcast', function(data) {
            document.getElementById("hell0").innerHTML += '<li>' + data.description + '</li>';
            // $('#hell0').append($('<li>').append($(data.description).append($('<li>');
            $('#hell0').scrollTop($('#hell0')[0].scrollHeight);




        });

    }); //end of connect event.
    var to;
    var tocount;
    var fromcount;
    socket.on('countynew', function(data) {

        console.log(data.rec);
        console.log(data.count);
        console.log(data.fro);
        to = data.rec;
    });

    //receiving online users stack from server node js.
    socket.on('onlineStack', function(stack) {

        $('#list').empty();

        $('#list').append($('<li>').append($('<button id="ubtn" class="btn btn-danger btn-block btn-lg"></button>').text("Group").css({
            "font-size": "18px"
        })));
        var txt_count;
        var totalOnline = 0;

        //FOR LOOP TO GET ALL THE ONLINE USERS 
        for (let user in stack) {


            //listing all users.

            if (user != username && stack[user] == "Online") {
                // totalOnline variable count counts how many users are in online 
                totalOnline++

                var fuser = user;
                var fusern = username;
                var tryit;

                // From here i am sending onlineusers list and owner (owner is the perosn who is using this script) to server using socket.io
                socket.emit('sendinguserandowner', {
                    onlineuser: fuser,
                    owner: fusern
                });

                //datahit contains  whom sent, how many messages sent and who send 
                socket.on('hitback', function(datahit) {        
                    tryit = datahit;

                //datahit.to contains your user name (The person who is currently using this script: owner) // CONSIDER X USING and he 
                //datahit.to conatins count of messages recieved from datahit.from
                //datahit.from contains online users for X // Y, Z, A, B....
                    console.log(datahit.to + " You recieved " + datahit.count + " from " + datahit.from);


                //HERE IS THE PROBLEM 
                 //HERE IS THE PROBLEM
                  //HERE IS THE PROBLEM
                   //HERE IS THE PROBLEM
                    if (datahit.from==fuser) {

                        $('.county')
                            .text(datahit.count) // EVERY TIME IT RETURNS LAST OCCORANCE VALUE example : if for loop iterating 3 times like 0,1,2 i am able to see 0,1,2 if i do console.log but if i append to HTML i am getting 2 only
                            .css({
                                "float": "center",
                                "color": "#000",
                                "font-size": "25px"
                            });



                    }
                     //UP TO HERE IS THE PROBLEM
                    //UP TO HERE IS THE PROBLEM
                    //UP TO HERE IS THE PROBLEM
                    //UP TO HERE IS THE PROBLEM


                });


                var txt_count = $('<span class="county"></span>')
                    .text("-") 
                    .css({
                        "float": "center",
                        "color": "#000",
                        "font-size": "25px"
                    });
                var txt1 = $('<button id="ubtn" class="btn btn-success  btn-md">').text(user).css({ // HERE I AM APPENDING ONLINE USER'S NAME TO BUTTON
                    "font-size": "18px"
                });
                //console.log(user+"   "+username); // to view in browser console
                var txt2 = $('<span class="badge"></span>').text("*" + stack[user]).css({ // HERE IS THE STATUS OF ONLINE USER WHETHER HE/SHE is ONLINE OR OFFLINE
                    "float": "right",
                    "color": "#009933",
                    "font-size": "18px"
                });




                $('#list').append($('<li>').append(txt1, txt2, txt_count));// ITERATE BY ABOVE FOR LOOP AND APPAEND ONLINE USER NAMES AND THEIR STATUS TO BUTTONS


            }
            $('#totalOnline').text(totalOnline);

        } //END OF THE FOR.

        $('#scrl1').scrollTop($('#scrl1').prop("scrollHeight"));
    }); //end of receiving onlineStack event.


    //on button click function.
    $(document).on("click", "#ubtn", function() {

        //empty messages.
        $('#messages').empty();
        $('#typing').text("");
        msgCount = 0;
        noChat = 0;
        oldInitDone = 0;

        //assigning friends name to whom messages will send,(in case of group its value is Group).
        toUser = $(this).text();
        socket.emit('readed', {
            readed: toUser
        });
        //showing and hiding relevant information.
        $('#frndName').text(toUser);
        $('#initMsg').hide();
        $('#chatForm').show(); //showing chat form.
        $('#sendBtn').hide(); //hiding send button to prevent sending of empty messages.

        //assigning two names for room. which helps in one-to-one and also group chat.
        if (toUser == "Group") {
            var currentRoom = "Group-Group";
            var reverseRoom = "Group-Group";
        } else {
            var currentRoom = username + "-" + toUser;
            var reverseRoom = toUser + "-" + username;
        }
        //event to set room and join.
        socket.emit('set-room', {
            name1: currentRoom,
            name2: reverseRoom
        });

    }); //end of on button click event.

    //event for setting roomId.
    socket.on('set-room', function(room) {
        //empty messages.
        $('#messages').empty();
        $('#typing').text("");
        msgCount = 0;
        noChat = 0;
        oldInitDone = 0;
        //assigning room id to roomId variable. which helps in one-to-one and group chat.
        roomId = room;
        console.log("roomId : " + roomId);
        //event to get chat history on button click or as room is set.
        socket.emit('old-chats-init', {
            room: roomId,
            username: username,
            msgCount: msgCount
        });

    }); //end of set-room event.

    //on scroll load more old-chats.
    $('#scrl2').scroll(function() {

        if ($('#scrl2').scrollTop() == 0 && noChat == 0 && oldInitDone == 1) {
            $('#loading').show();
            socket.emit('old-chats', {
                room: roomId,
                username: username,
                msgCount: msgCount
            });
        }

    }); // end of scroll event.

    //listening old-chats event.
    socket.on('old-chats', function(data) {

        if (data.room == roomId) {
            oldInitDone = 1; //setting value to implies that old-chats first event is done.
            if (data.result.length != 0) {
                $('#noChat').hide(); //hiding no more chats message.
                for (var i = 0; i < data.result.length; i++) {
                    //styling of chat message.
                    var chatDate = moment(data.result[i].createdOn).format("MMMM Do YYYY, hh:mm:ss a");
                    var txt1 = $('<span></span>').text(data.result[i].msgFrom + " : ").css({
                        "color": "#006080"
                    });
                    var txt2 = $('<span></span>').text(chatDate).css({
                        "float": "right",
                        "color": "#a6a6a6",
                        "font-size": "16px"
                    });
                    var txt3 = $('<p></p>').append(txt1, txt2);
                    var txt4 = $('<p></p>').text(data.result[i].msg).css({
                        "color": "#000000"
                    });


                    //showing chat in chat box.
                    $('#messages').prepend($('<li>').append(txt3, txt4));
                    msgCount++;

                } //end of for.
                // $('#county').text(msgCount);
                console.log(msgCount);

            } else {
                $('#noChat').show(); //displaying no more chats message.
                noChat = 1; //to prevent unnecessary scroll event.
            }
            //hiding loading bar.
            $('#loading').hide();

            //setting scrollbar position while first 5 chats loads.
            if (msgCount <= 5) {
                $('#scrl2').scrollTop($('#scrl2').prop("scrollHeight"));
            }
        } //end of outer if.

    }); // end of listening old-chats event.

    // keyup handler.
    $('#myMsg').keyup(function() {
        if ($('#myMsg').val()) {
            $('#sendBtn').show(); //showing send button.
            socket.emit('typing');
        } else {
            $('#sendBtn').hide(); //hiding send button to prevent sending empty messages.
        }
    }); //end of keyup handler.

    //receiving typing message.
    socket.on('typing', function(msg) {
        var setTime;
        //clearing previous setTimeout function.
        clearTimeout(setTime);
        //showing typing message.
        $('#typing').text(msg);
        //showing typing message only for few seconds.
        setTime = setTimeout(function() {
            $('#typing').text("");
        }, 3500);
    }); //end of typing event.




    //sending message.
    $('form').submit(function() {
        socket.emit('chat-msg', {
            msg: $('#myMsg').val(),
            msgTo: toUser,
            date: Date.now()
        });
        console.log("2. TO:" + toUser + " Message is:" + $('#myMsg').val() + "  Line 202 in ScriptForChat file")
        $('#myMsg').val("");
        $('#sendBtn').hide();

        return false;
    }); //end of sending message.





    //receiving messages.
    unread = 0;
    socket.on('chat-msg', function(data) {
        unread++;
        //styling of chat message.
        var chatDate = moment(data.date).format("MMMM Do YYYY, hh:mm:ss a");
        var txt1 = $('<span></span>').text(data.msgFrom + " : ").css({
            "color": "#006080"
        });
        var txt2 = $('<span></span>').text(chatDate).css({
            "float": "right",
            "color": "#a6a6a6",
            "font-size": "16px"
        });
        var txt3 = $('<p></p>').append(txt1, txt2);
        var txt4 = $('<p></p>').text(data.msg).css({
            "color": "#000000"
        });


        console.log("2. From:" + data.msgFrom + " Message is:" + data.msg)
        //showing chat in chat box.
        $('#messages').append($('<li>').append(txt3, txt4));
        msgCount++;
        console.log(msgCount);
        $('#typing').text("");
        $('#scrl2').scrollTop($('#scrl2').prop("scrollHeight"));
    }); //end of receiving messages.



    //on disconnect event.
    //passing data on connection.
    socket.on('disconnect', function() {


        //showing and hiding relevant information.
        $('#list').empty();
        $('#messages').empty();
        $('#typing').text("");
        $('#frndName').text("Disconnected..");
        $('#loading').hide();
        $('#noChat').hide();
        $('#initMsg').show().text("...Please, Refresh Your Page...");
        $('#chatForm').hide();
        msgCount = 0;
        noChat = 0;
    }); //end of connect event
}); //end of function

Below is the SERVER SIDE NODE JS FILE WITH SOCKET.IO

var socketio = require('socket.io');
var mongoose = require('mongoose');
var events = require('events');
var _ = require('lodash');
var eventEmitter = new events.EventEmitter();

//adding db models
require('../app/models/user.js');
require('../app/models/chat.js');
require('../app/models/room.js');

//using mongoose Schema models
var userModel = mongoose.model('User');
var chatModel = mongoose.model('Chat');
var roomModel = mongoose.model('Room');


//reatime magic begins here
module.exports.sockets = function (http) {
    io = socketio.listen(http);


    //setting chat route
    var ioChat = io.of('/chat');
    var userStack = {};

    var oldChats, sendUserStack, setRoom;
    var userSocket = {};

    //socket.io magic starts here
    ioChat.on('connection', function (socket) {
        console.log("socketio chat connected.");

        //function to get user name
        socket.on('set-user-data', function (username) {
            console.log(username + "  logged In");


            //HERE IS THE MAIN CODE WHICH WILL EXECUTE WHEN sendinguserandowner emits from CLIENT.JS
            socket.on('sendinguserandowner', function (data) {
                // Here using mongo i am retriving count of messages 
                var MongoClient = require('mongodb').MongoClient;
                var url = "mongodb://localhost:27017/";

                MongoClient.connect(url, function (err, db) {
                    if (err) throw err;
                    var dbo = db.db("rio_chat");
                    dbo.collection("chats").find({
                        msgFrom: data.onlineuser,
                        msgTo: data.owner,
                        unread: "1"
                    }).count(function (err, result) {
                        if (err) throw err;

                        //DATAHIT IN CLIENT SIDE IS SAME AS HERE but this is for server side console
                        console.log(data.owner + " You recieved " + result + " from " + data.onlineuser);

                        // HERE I AM HITTING BACK TO BROWSER 
                        socket.emit('hitback', {
                            to: data.owner,
                            count: result,
                            from: data.onlineuser
                        });
                        db.close();
                    });
                });


            });


            //storing variable.
            socket.username = username;
            userSocket[socket.username] = socket.id;

            socket.broadcast.emit('broadcast', {
                description: username + ' Logged In'
            });


            //getting all users list
            eventEmitter.emit('get-all-users');

            //sending all users list. and setting if online or offline.
            sendUserStack = function () {
                for (i in userSocket) {
                    for (j in userStack) {
                        if (j == i) {
                            userStack[j] = "Online";


                        }
                    }
                }
                //for popping connection message.
                ioChat.emit('onlineStack', userStack);
            } //end of sendUserStack function.

        }); //end of set-user-data event.

        //setting room.
        socket.on('set-room', function (room) {
            //leaving room.
            socket.leave(socket.room);
            //getting room data.
            eventEmitter.emit('get-room-data', room);
            //setting room and join.
            setRoom = function (roomId) {
                socket.room = roomId;
                console.log("roomId : " + socket.room);
                socket.join(socket.room);
                ioChat.to(userSocket[socket.username]).emit('set-room', socket.room);
            };

        }); //end of set-room event.
        //emits event to read old-chats-init from database.
        socket.on('old-chats-init', function (data) {
            eventEmitter.emit('read-chat', data);
        });
        //emits event to read old chats from database.
        socket.on('old-chats', function (data) {
            eventEmitter.emit('read-chat', data);
        });
        //sending old chats to client.
        oldChats = function (result, username, room) {
            ioChat.to(userSocket[username]).emit('old-chats', {
                result: result,
                room: room
            });
        }
        //showing msg on typing.
        socket.on('typing', function () {
            socket.to(socket.room).broadcast.emit('typing', socket.username + " : is typing...");
        });
        socket.on('readed', function (data) {
            console.log("TO : " + data.readed + " From : " + socket.username + "");
        });
        //for showing chats.
        socket.on('chat-msg', function (data) {
            //emits event to save chat to database.
            eventEmitter.emit('save-chat', {
                msgFrom: socket.username,
                msgTo: data.msgTo,
                msg: data.msg,
                room: socket.room,
                unread: 1,
                date: data.date
            });
            //emits event to send chat msg to all clients.
            ioChat.to(socket.room).emit('chat-msg', {
                msgFrom: socket.username,
                msg: data.msg,
                date: data.date
            });
        });
        //for popping disconnection message.
        socket.on('disconnect', function () {

            console.log(socket.username + "  logged out");
            socket.broadcast.emit('broadcast', {
                description: socket.username + ' Logged out'
            });


            console.log("chat disconnected.");

            _.unset(userSocket, socket.username);
            userStack[socket.username] = "Offline";

            ioChat.emit('onlineStack', userStack);
        }); //end of disconnect event.

    }); //end of io.on(connection).
    //end of socket.io code for chat feature.

    //database operations are kept outside of socket.io code.
    //saving chats to database.
    eventEmitter.on('save-chat', function (data) {

        // var today = Date.now();

        var newChat = new chatModel({

            msgFrom: data.msgFrom,
            msgTo: data.msgTo,
            msg: data.msg,
            room: data.room,
            unread: 1,
            createdOn: data.date

        });

        newChat.save(function (err, result) {
            if (err) {
                console.log("Error : " + err);
            } else if (result == undefined || result == null || result == "") {
                console.log("Chat Is Not Saved.");
            } else {
                console.log("Chat Saved.");
                //console.log(result);
            }
        });

    }); //end of saving chat.

    //reading chat from database.
    eventEmitter.on('read-chat', function (data) {

        chatModel.find({})
            .where('room').equals(data.room)
            .sort('-createdOn')
            .skip(data.msgCount)
            .lean()
            .limit(5)
            .exec(function (err, result) {
                if (err) {
                    console.log("Error : " + err);
                } else {
                    //calling function which emits event to client to show chats.
                    oldChats(result, data.username, data.room);
                }
            });
    }); //end of reading chat from database.

    //listening for get-all-users event. creating list of all users.
    eventEmitter.on('get-all-users', function () {
        userModel.find({})
            .select('username')
            .exec(function (err, result) {
                if (err) {
                    console.log("Error : " + err);
                } else {
                    //console.log(result);
                    for (var i = 0; i < result.length; i++) {
                        userStack[result[i].username] = "Offline";
                    }
                    //console.log("stack "+Object.keys(userStack));
                    sendUserStack();
                }
            });
    }); //end of get-all-users event.

    //listening get-room-data event.
    eventEmitter.on('get-room-data', function (room) {
        roomModel.find({
            $or: [{
                name1: room.name1
            }, {
                name1: room.name2
            }, {
                name2: room.name1
            }, {
                name2: room.name2
            }]
        }, function (err, result) {
            if (err) {
                console.log("Error : " + err);
            } else {
                if (result == "" || result == undefined || result == null) {

                    var today = Date.now();

                    newRoom = new roomModel({
                        name1: room.name1,
                        name2: room.name2,
                        lastActive: today,
                        createdOn: today
                    });

                    newRoom.save(function (err, newResult) {

                        if (err) {
                            console.log("Error : " + err);
                        } else if (newResult == "" || newResult == undefined || newResult == null) {
                            console.log("Some Error Occured During Room Creation.");
                        } else {
                            setRoom(newResult._id); //calling setRoom function.
                        }
                    }); //end of saving room.

                } else {
                    var jresult = JSON.parse(JSON.stringify(result));
                    setRoom(jresult[0]._id); //calling setRoom function.
                }
            } //end of else.
        }); //end of find room.
    }); //end of get-room-data listener.
    //end of database operations for chat feature.
    //to verify for unique username and email at signup.
    //socket namespace for signup.
    var ioSignup = io.of('/signup');

    var checkUname, checkEmail; //declaring variables for function.

    ioSignup.on('connection', function (socket) {
        console.log("signup connected.");

        //verifying unique username.
        socket.on('checkUname', function (uname) {
            eventEmitter.emit('findUsername', uname); //event to perform database operation.
        }); //end of checkUname event.

        //function to emit event for checkUname.
        checkUname = function (data) {
            ioSignup.to(socket.id).emit('checkUname', data); //data can have only 1 or 0 value.
        }; //end of checkUsername function.

        //verifying unique email.
        socket.on('checkEmail', function (email) {
            eventEmitter.emit('findEmail', email); //event to perform database operation.
        }); //end of checkEmail event.

        //function to emit event for checkEmail.
        checkEmail = function (data) {
            ioSignup.to(socket.id).emit('checkEmail', data); //data can have only 1 or 0 value.
        }; //end of checkEmail function.

        //on disconnection.
        socket.on('disconnect', function () {
            console.log("signup disconnected.");
        });

    }); //end of ioSignup connection event.

    //database operations are kept outside of socket.io code.
    //event to find and check username.
    eventEmitter.on('findUsername', function (uname) {

        userModel.find({
            'username': uname
        }, function (err, result) {
            if (err) {
                console.log("Error : " + err);
            } else {
                //console.log(result);
                if (result == "") {
                    checkUname(1); //send 1 if username not found.
                } else {
                    checkUname(0); //send 0 if username found.
                }
            }
        });

    }); //end of findUsername event.

    //event to find and check username.
    eventEmitter.on('findEmail', function (email) {

        userModel.find({
            'email': email
        }, function (err, result) {
            if (err) {
                console.log("Error : " + err);
            } else {
                //console.log(result);
                if (result == "") {
                    checkEmail(1); //send 1 if email not found.
                } else {
                    checkEmail(0); //send 0 if email found.
                }
            }
        });
    }); //end of findUsername event.
    return io;};

Console.log & Appending to HTML DIFFERENCE

  • 1
    Take a look at this post: https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – ema Apr 18 '18 at 05:23
  • Hi Ema, Thanks for your comment ill try before that... Give me clarity in one thing... Why this problem occur.. . Could you please explain –  Apr 18 '18 at 06:01

1 Answers1

0

The problem is with updating dom elements using the class .county in your frontend javascript code while receiving hitback event

$('.county')
      .text(datahit.count) // EVERY TIME IT RETURNS LAST OCCORANCE VALUE example : if for loop iterating 3 times like 0,1,2 i am able to see 0,1,2 if i do console.log but if i append to HTML i am getting 2 only
      .css({
          "float": "center",
          "color": "#000",
          "font-size": "25px"
      });

What is happening here?

When datahit.count is 251 javascript updates 251 to the element with class .county.

When datahit.count is 33 javascript updates 33 to the element with class .county. During this operation, all the elements with class .county are updated in DOM. At this point of time, the 251 gets changed to 33 .

This is how css works.

You have to change your rendering logic to fix this.

Update

To fix this, when you emit hitback event from the server , you could send both current and previous notifications using JsonArray (you have to do this everytime when you emit hitback event) and re-render the html elements related to this notification everytime hitback is received in the client.

Your current logic updates the dom for a single input emitted by hitback event. This should be modified.

divine
  • 4,746
  • 3
  • 27
  • 38
  • Could you explain me some logic to implement or any other way to implement to solve my problem. –  Apr 18 '18 at 07:41
  • Thanks for your comment divine... Good explanation.. ill try .. but is there any other way also to implement if yes could you please give me some references or simple basic example... And please take a look on Ema comment .. is that will help?? –  Apr 18 '18 at 07:59
  • @JanakiRajeshDuvvuri ema's comment is not relevant to the solution you need. – divine Apr 18 '18 at 08:02
  • Ok and is there any simple example you can give me for that JSON thing??.. it will help me a lot .. –  Apr 18 '18 at 08:04
  • @JanakiRajeshDuvvuri to implement these kind of dom re-rendering scenario's with ease you have to use html templates. mustache js is one of the popular library for this purpose. take a look at it. http://coenraets.org/blog/2011/12/tutorial-html-templates-with-mustache-js/ – divine Apr 18 '18 at 08:06
  • @JanakiRajeshDuvvuri a plain old array of json object will do the job. Just google it. – divine Apr 18 '18 at 08:08
  • @JanakiRajeshDuvvuri your original question has been answered. if you find my post useful you can accept my answer. AND , Kindly create a new question if you have anyother questions. – divine Apr 18 '18 at 08:09
  • I'll try all the ways u said and I'll notify u back.. i hope your comment make sense to my code –  Apr 18 '18 at 08:10