Build a simple chat room with Node.js, Socket.IO and Express (part 3)

Display the message from system

In part 2, we already logged the message `[DISPLAY NAME] has joined.`
In the HTML, we have 3 fields: "Display name", "Create at" and "Body":

so we update the source code:

I don't want to load the page after user clicks "Join now" button, so I copy the code from chat.html to index.html (of course, remove dynamic content). I use Moment.js and Mustache.js

  • Moment.js: parse, validate, manipulate, and display dates and times in JavaScript.
  • Mustache.js: a logic-less template syntax. It can be used for HTML, config files, source code - anything. It works by expanding tags in a template using values provided in a hash or object.


Update on newMessage code:

Open browser and test, Join with name "Alice":

Now, let's dynamic name of the chat room!

Dynamic name of the chat room




Display the message from users

On form submit, we send the message to server:


Server listen and generate message:

We create "users" variable to store all users for each room.

Use class or not, it depends on yourself.

Daisy joins room #KIDS???
==> room #MOM doesn't take care :D

Show "people in chat room"

When a user joined, the server sends the "people in chat room" list to client:


and when user disconnected, the server also sends the "people in chat room" list to client:

On client side:



Open your browser and test:


We already had some issues with the scroll bar when received new messages and we didn't check the unique display name. We will do in part 4. Now, get the latest code for today:

server.js
var express=require('express');const path=require('path');const http=require('http');const socketIO=require('socket.io');const _=require('lodash');const validator=require('validator');const port=process.env.PORT||3000;var app=express();app.use(express.static(path.join(__dirname,'./public')));var server=http.createServer(app);var io=socketIO(server);generateMessage=(from,body)=>{return{from,body,createdAt:new Date().getTime()}};class Users{constructor(){this.users=[];} addUser(id,display_name,room){var user={id,display_name,room};this.users.push(user);return user;} getUser(id){return this.users.filter((user)=>user.id===id)[0]} removeUser(id){var user=this.getUser(id);if(user){this.users=this.users.filter((user)=>user.id!==id);} return user;} getUsers(room){var users=this.users.filter((user)=>user.room===room);return users.map((user)=>user.display_name);}} var users=new Users();io.on('connection',(socket)=>{console.log('New user connected');socket.on('join',(params,callback)=>{var isRoomValid=_.isString(params.room)&&validator.isIn(params.room,['mom','kids','sell_off']);var isDisplaynameValid=_.isString(params.display_name)&&!validator.isEmpty(params.display_name.trim())&&validator.isAlpha(params.display_name)&&validator.isLength(params.display_name,{min:3,max:15});if(!isRoomValid){return callback('Please choose a room from dropdown list.');} if(!isDisplaynameValid){return callback('Please enter a valid display name (3-15 alphabetic characters).');} socket.join(params.room);users.removeUser(socket.id);users.addUser(socket.id,params.display_name,params.room);io.to(params.room).emit('updateUserList',users.getUsers(params.room));io.to(params.room).emit('newMessage',generateMessage('System',`${params.display_name}has joined.`));callback();});socket.on('createMessage',(message,callback)=>{var user=users.getUser(socket.id);if(user&&!validator.isEmpty(message.trim())&&validator.isLength(message,{min:3,max:160})){io.to(user.room).emit('newMessage',generateMessage(user.display_name,message));} callback();});socket.on('disconnect',()=>{var user=users.removeUser(socket.id);if(user){io.to(user.room).emit('updateUserList',users.getUsers(user.room));io.to(user.room).emit('newMessage',generateMessage('System',`${user.display_name}has left.`));}});});server.listen(port);

index.html
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Join</title><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"><link rel="stylesheet" href="/css/style.css"></head><body><div class="join-wrapper" id="join-wrapper"><form class="form-horizontal" id="frm-join"><h2 class="text-center">Please Join</h2><div id="join-error-msg" class="alert alert-danger hidden" role="alert"></div><div class="form-group"> <label for="txt-display-name" class="col-sm-4 control-label">Display name</label><div class="col-sm-8"> <input type="text" required class="form-control" id="txt-display-name"></div></div><div class="form-group"> <label for="sl-room" class="col-sm-4 control-label">Room</label><div class="col-sm-8"> <select class="form-control" id="sl-room"><option value="mom">Mom</option><option value="kids">Kids</option><option value="sell_off">Sell off</option> </select></div></div><div class="form-group"><div class="col-sm-offset-4 col-sm-8"> <button type="submit" class="btn btn-primary">Join now</button></div></div></form></div><div class="page-wrapper hidden" id="chat-room-wraper"><div class="container"><div class="row"><div class="col-md-9 col-sm-9"><div class="green-bg"><div class="title main" id="chat-room-name"></div><div id="messages"></div><div id="chat-box"><form id="frm-chat"><div class="input-group"> <input type="text" id="txt-message" class="form-control"> <span class="input-group-btn"> <button class="btn btn-primary" type="submit">Send</button> </span></div></form></div></div></div><div class="col-md-3 col-sm-3 sidebar"><div class="green-bg"><div class="title"> People in chatroom</div><ul class="list-group" id="users"></ul></div></div></div></div></div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/template" id="message-template"><div class="item"><p class="author">{{from}}<span class="date">({{createdAt}})</span></p><p>{{body}}</p></div></script> <script type="text/template" id="users-template"><li class="list-group-item">{{name}}</li></script> <script>var socket=io();socket.on('newMessage',function(message){var html=Mustache.render($('#message-template').html(),{from:message.from,body:message.body,createdAt:moment(message.createdAt).format('MMM Do, YYYY HH:mm')});$('#messages').append(html);});var selectedRoom;$(function(){$('#frm-join').on('submit',function(e){e.preventDefault();selectedRoom=$('#sl-room option:selected').val();socket.emit('join',{room:selectedRoom,display_name:$('#txt-display-name').val()},function(err){if(err){$('#join-error-msg').text(err);$('#join-error-msg').removeClass('hidden');} else{$('#chat-room-name').text('#'+$('#sl-room option:selected').text());$('#join-wrapper').remove();$('#chat-room-wraper').removeClass('hidden');}});});$('#frm-chat').on('submit',function(e){e.preventDefault();socket.emit('createMessage',$('#txt-message').val(),function(){$('#txt-message').val('');});});socket.on('updateUserList',function(users){$('#users').html('');users.forEach(function(user){var html=Mustache.render($('#users-template').html(),{name:user});$('#users').append(html);});});});</script> </body></html>

See you then!

No comments:

Post a Comment