Matoy Matoy - 1 month ago 10
Javascript Question

angular and socket io class manipulation

I am bulding a chat app with angular on the front end and socket io for the chat platform.

I want to have a diffenet css class for the sender in the chat (the one who sends the message in the chat) and for the side that recieves the message.

to do that I've created 2 classes in my css: "sender" and "reciever".

I want that when my socket gets a message from the "sender" (aka "the speaker side") - the li class will be "sender". when the socket gets a message from outside (some other member in the chat the is not me) the class will be "reciever".

like the green and white here: http://www.androidpolice.com/wp-content/uploads/2015/07/nexus2cee_whatsapp-middle-finger-2.png

I know that I can change the class in angluar using the ng-class directive.

The problem is that when I do the class manipulate all the messages in my chat became a part of that class (I am using ng-repeat directive).

and what I want is that 1 message will be class A and other message will be from class B and 3rd message will be class A.... so on...

I know that I should somehow use the ng-class propertie like:

<li ng-class={'sender' : message.sender, 'btn-off' : !message.sender} ng-repeat="message in messages track by $index">{{message}}</li>


but how can I get the message object (which is a string to include a boolean as well?

can you please help me to figure out how I can write the code for that?

this is my controler

mymodule.controller("cntrlChat", ['$scope', 'myService','$q','$timeout',
function($scope, myService,$q,$timeout){
var socket = io();
$scope.messages = [];
$scope.message_type="sender";
$scope.room= myService.get().room;
$scope.name= myService.get().name;
socket.emit('room', $scope.room);

$scope.submit=function(){
socket.emit('chat_message',{ room: $scope.room, msg: $scope.name+": "+$scope.insertedText });
$scope.message_type="sender";
$scope.messages.push($scope.name+": "+$scope.insertedText);
$scope.insertedText='';
return false;
}

socket.on('chat_message', function(msg){
$scope.$apply(function() {
$scope.message_type="receiver";
$scope.messages.push(msg);

});
});

socket.on('info_message', function(msg){
$scope.$apply(function() {
$scope.info=msg;
});
});


this is the server.js file:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var path = require('path');


app.use(express.static(path.join(__dirname, '/')));

app.get('/', function(req, res){
res.sendFile(__dirname + '/Index.html');
});

io.on('connection', function(socket){
io.emit('chat_message', "welcome");

socket.on('room', function(room) {
socket.join(room);
});

socket.on('chat_message', function(data){
socket.broadcast.to(data.room).emit('chat_message',data.msg);
});
socket.on('info_message', function(data){
socket.broadcast.to(data.room).emit('info_message',data.msg);
});

socket.on('disconnect', function(){
io.emit('chat message', "Bye");

});

});

http.listen((process.env.PORT || 3000), function(){
console.log('listening on *:3000 '+ __dirname);
});


this is the html for my chat:

<head>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="chat">
<div class="members">
{{users}}
</div>
<div class="messages">
<ul>
<li ng-class="message_type" ng-repeat="message in messages track by $index">{{message}}</li>

</ul>
</div>
<form ng-submit="submit()">
{{info}}
<br>
<input autocomplete="off" ng-model="insertedText" ng-change="isUserTyping()" type="text" />
<button type="button" ng-click="submit()">
Send
</button>
</form>
</div>
</body>

Answer

Ok let's take a crack at this.

On a high level description, we want the socket to pass messages between users.

The best way to ensure we know who sent what is to make sure the data we pass has the right details.

With that being said, you just need to turn the msg from a string to an object and pass that around with a flag (I used sender/receiver, you can use sender: true/false):

   $scope.submit=function(){
    //
    // change msg to an object (I don't know io well so it may need to be json encoded decoded?)
    //
    socket.emit('chat_message', { 
        room: $scope.room,
        msg: {
            text: $scope.name+": "+$scope.insertedText,
            status: 'sender'
        }
    });

    //
    // push the data obj to your messages array
    //
    $scope.messages.push(msg)

    $scope.insertedText='';
    return false; 
  }

  socket.on('chat_message', function(data){
    $scope.$apply(function() {
      //
      // we expect data to look like the data above except we'll change the flag when it's retrieved
      //
      data.status = 'received'
      $scope.messages.push(data);
    });
  });

  socket.on('info_message', function(msg){
    $scope.$apply(function() {
      $scope.info=msg;
    });
  });

We're now passing objects with flags to and from the node server without modification on the server side so that code doesn't need to change at all.

Finally, the angular part:

<div class="chat">
    <div class="members">
        {{users}}
    </div>
    <div class="messages">
        <ul>
            //
            // ng class stuff
            // (ternary) ng-class="message.status === 'received' ? 'class1' : 'class2'"
            //
            <li ng-class="{'class1': message.status === 'sender', 'class2': message.status === 'received'}" ng-repeat="message in messages track by $index">{{message.text}}</li>
        </ul>
    </div>
    <form ng-submit="submit()">
        {{info}}  
        <br>
        <input autocomplete="off" ng-model="insertedText" ng-change="isUserTyping()" type="text" />
        <button type="button" ng-click="submit()">
            Send
        </button>
    </form>
</div>