How to broadcast and get notification using Express, AngularJS, Socket.io? - javascript

I am trying to make notification system. To demonstrate this, User 1 is sending friend request to User 2. I am using express.js, angularjs and socket.io. On click of the button User1 sends request. On end of User2, there is a socket,on() which is listening on friend-request event. But when I am broadcasting, the other user is not able to receive any message.
app.js (Node Server File)
var express = require('express'),
app = express();
var port = process.env.PORT || 3000;
var io = require('socket.io').listen(app.listen(port));
require('./config')(app,io);
require('./routes')(app,io);
config.js
// This file handles the configuration of the app.
// It is required by app.js
var express = require('express');
module.exports = function(app, io){
// Set .html as the default template extension
app.set('view engine', 'html');
// Initialize the ejs template engine
app.engine('html', require('ejs').renderFile);
// Tell express where it can find the templates
app.set('views', __dirname + '/views');
// Make the files in the public folder available to the world
app.use(express.static(__dirname + '/public'));
};
routes.js (Emitting Friend Request From this File)
var gravatar = require('gravatar');
var mysql = require('mysql');
// This is needed if the app is run on heroku:
var connection = mysql.createConnection({
host : "localhost",
user : "root",
password : "",
database : "two_way_demo"
});
connection.connect(function(error){
if(error)
{
console.log("Problem with MySQL"+error);
}
else {
console.log("Connected with Database");
}
});
module.exports = function(app,io){
app.get('/',function(req,res){
res.render('index');
});
app.get('/create', function(req,res){
// Generate unique id for the room
var id = Math.round((Math.random() * 1000000));
// Redirect to the random room
res.redirect('/chat/'+id);
});
app.get('/home/:id', function(req,res){
// Render the chant.html view
res.render('home');
});
// Initialize a new socket.io application, named 'chat'
var chat = io.on('connection', function (socket) {
socket.on('get-user-id',function(data){
connection.query("SELECT * from user_info WHERE email='"+data.userEmail+"'",function(err,rows){
if(err)
{
console.log("Problem with MySQL"+err);
}
else
{
//console.log(rows);
JSON.stringify(rows);
socket.emit('user-id',rows);
}
});
});
socket.on('send-request',function(data){
console.log(data);
*********************************************************************
// Tried the emit here but its not working
//io.emit('friend request', {
// receiverid: data.receiverid
//});
*********************************************************************
});
});
}
angular-code.js (angular code file)
$(function () {
var app = angular.module("notificationApp", []);
app.controller("chatCTRL", ["$scope", "$http", "$interval", function ($scope, $http, $interval) {
// connect to the socket
//var socket = io();
//socket.on('connect', function () {
// io.on('friend request', function (data) {
// alert("here")
// });
//});
$scope.senderId = Number(window.location.pathname.match(/(\d+)$/)[1]);
$scope.sendrequest = function (senderid, receiverid) {
var socket = io();
socket.on('connect', function () {
socket.emit('send-request', {
senderid: senderid,
receiverid : receiverid
});
});
}
}]);
app.controller("loginCTRL", ["$scope", "$http", "$interval", "$window", function ($scope, $http, $interval, $window) {
$scope.sendLogin = function () {
var socket = io();
socket.on('connect', function () {
socket.emit('get-user-id', {
userEmail: $scope.hisEmail
});
});
socket.on('connect', function () {
socket.on('user-id', function (data) {
$scope.UserId = data[0].user_id;
$window.location = "http://localhost:3000/home/" + $scope.UserId;
});
});
}
}]);
}());
home.html
<!DOCTYPE html>
<html ng-app="notificationApp">
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body ng-controller="chatCTRL">
<h1>welcome</h1>
<div id="createbutton">
<div id="little"><button ng-click="sendrequest(senderId,6)">Send Friend Request to User#6</button></div>
</div>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="../angular/angular.js"></script>
<script src="../angular/common_angular.js"></script>
</body>
</html>

Some client side architecture things:
In most cases on angular client side it is better to move your socket connection to service. And make connection when service is initialized (service is singleton, therefore there will be one connection on start) and inject this service in your controllers.
It may be convenient to create some parent abstract controller with
all socket listeners, therefore whether angular controller is active, all listeners are watching. When parent controller get data from socket it can broadcast it to children controllers
In your commented code you have:
//var socket = io();
//socket.on('connect', function () {
// io.on('friend request', function (data) {
// alert("here")
// });
//});
change it to this (if you make connection in service you should omit connect part):
var socket = io();
socket.on('connect', function () {
socket.on('friend request', function (data) {
alert("here")
});
});
Backend:
In your commented code you have:
//io.emit('friend request', {
// receiverid: data.receiverid
//});
You should use socket's from var chat = io.on('connection', function (socket) {... to emit instead of io.emit
Create array variable where you will store all your sockets with users id before connection part:
var socketList = [];
var chat = io.on('connection', function (socket) {
socketList.push({id:someId,socket:socket})
...
}
Now in send-request user should send id of his frient (we have to know which user should be notified- of course we can notify everybody):
socket.on('send-request',function(data){
socketList.forEach(function(soc){
if(soc.id === someId){
soc.socket.emit('friend request', {
receiverid: data.receiverid
})
}
});
Also i don't like this part receiverid: data.receiverid, because it means that taget user get id of receiver from receiver client side. And this may be unsafe (user can change his id and send some other id). I prefere to create id in server side and when user A send notification to user B I get user A id from server variable.
Some time age I create simple prototype of chat application (angular and express), there are some things which I mention here. I you have still problems with your application go there and check my code :
https://github.com/uhlryk/chat-prototype

Related

Close the socket.io connection when user changes route in Angular 8

Socket.io closes the connection when user try to close the tab or window but it does not when user navigate to other route in same angular app, the disconnect event will not fire in that case.
server-side:
io.on('connection', function (socket) {
console.log('a user connected');
socket.on('disconnect', function () {
console.log('user disconnected');
});
});
the socket.disconnect() method will not work.
I am trying to use CanDisconnect routing guards but does not know how to use that to close the connection.
server side:
// dependencies
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
//importing routes
var loginRouter = require('./routes/loginRoutes');
var chatRouter = require('./routes/chatRoutes');
app.use('/', loginRouter); // applying routes to the app
app.use('/', chatRouter); // applying routes to the app
// starting the server
http.listen(3000, function(){
console.log('server is listening on port '+PORT);
});
// socket.io ===================================
io_public = io.of('/public'); // namespace public
io_public.on('connection', function(socket){
socket.on('adduser', function(username){
socket.username = username;
socket.room = 'public'; //assign default public room
socket.join(socket.room);
socket.emit('server', 'you have connected to a public room');
socket.broadcast.to('public').emit('server',socket.username + ' has connected to this room');
});
socket.on('disconnect', function(){
socket.broadcast.to('public').emit('server',socket.username + ' has left the room');
socket.leave(socket.room);
});
});
client side:
var socket = io('http://localhost:3000/public');
var user = Cookies.get('user');
socket.on('connect', function(){
socket.emit('adduser', user);
});
socket.on('server',function(msg){
$('#conversation').append('<li> <b>Server: </b>'+msg+'</li>');
});
I am loading this client script in home component on dashboard/home, when user moves to dashboard/contacts it does not disconnect. It also create duplicate socket listeners also because this client script will reloaded every time the component is loaded.
starting state
move to contact routes:
Reloading home component
You can generate an Angular service for connecting and disconnecting. Call the service to Connect on the ngOnInit on the components you want and then call the disconnect method on the ngOnDestroy method.
You can also try to do the same implementing Guards like you said.
Inside component.ts
ngOnInit() {
this.chatService.connect()
}
ngOnDestroy(){
this.chatService.disconnectSocket();
}
Inside ChatService
import { io, Socket } from "socket.io-client";
private socket: Socket;
constructor() {
this.socket = io("http://localhost:4000");
}
connect(){
this.socket.connect()
}
disconnectSocket(){
this.socket.disconnect()
}

Socket.io - Connect from client to server via https

I have created a socket.io chat application on my virtual server (Ubuntu), which runs as an systemd service and which is active running.
My server.js is located in:
/var/www/vhosts/mywebpage.de/w1.mywebpage.de/chat/
The server.js looks like this:
const io = require('socket.io')(3055);
io.on('connection', function(socket) {
// When the client emits 'addUser', this listens and executes
socket.on('addUser', function(username, room) {
...
});
// When the client emits 'sendMessage', this listens and executes
socket.on('sendMessage', function(msg) {
...
});
// Disconnect the user
socket.on('disconnectUser', function(username, room) {
...
});
});
In my website (https) I try to connect as follow:
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script type="text/javascript">
var loSocket;
$(document).ready(function() {
if(typeof(loSocket) == 'undefined') {
loSocket = io('https://w1.mywebpage.de:3055', {
reconnectionAttempts: 5,
forceNew: true
});
}
});
</script>
But I can't get a valid connection.
The developer tools say this:
(failed) ERR_CONNECTION_CLOSED with initiator polling-xhr.js:264.
What could be the error ?
From what I have done in the past I would create a https server which serves the SSL cert and create the socket server using the https server you created, this will allow you to connect via https and you will need to enable secure on socketio (use this question as a ref)
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Use this code as ref on how to create a socketio server using http
You can find this code on the socket.io docs
NOTE: You will need to you https not http like shown in the example

How do I properly emit data to server from client using Node.js?

When the client connects to the server a message is supposed to be emitted to the console. I'm not getting any errors so I'm confused as to what my problem actually is.
Server: As you can see the client connects.
Client: The message doesn't appear in the console.
(Forgive me for the links, I don't have 10 reputation)
How do I get the message to print to the console?
I've read other posts like this one, but they weren't helpful :(
When you do io.connect(), that call is asynchronous and not immediate. You cannot immediately emit to the server until the client generates the connect event:
var socket = io.connect()
socket.on('connect', function() {
// it is safe to call `.emit()` here
socket.emit("sndMsg", someData);
});
index.html
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script>
var socket = io();
socket.on('welcome', function(data) {
addMessage(data.message);
// Respond with a message including this clients' id sent from the server
socket.emit('i am client', {data: 'foo!', id: data.id});
});
socket.on('time', function(data) {
addMessage(data.time);
});
socket.on('error', console.error.bind(console));
socket.on('message', console.log.bind(console));
function addMessage(message) {
var text = document.createTextNode(message),
el = document.createElement('li'),
messages = document.getElementById('messages');
el.appendChild(text);
messages.appendChild(el);
}
</script>
</head>
<body>
<ul id='messages'></ul>
</body>
</html>
server.js
var http = require('http'),
fs = require('fs'),
// NEVER use a Sync function except at start-up!
index = fs.readFileSync(__dirname + '/index.html');
// Send index.html to all requests
var app = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(index);
});
// Socket.io server listens to our app
var io = require('socket.io').listen(app);
// Send current time to all connected clients
function sendTime() {
io.emit('time', { time: new Date().toJSON() });
}
// Send current time every 10 secs
setInterval(sendTime, 10000);
// Emit welcome message on connection
io.on('connection', function(socket) {
// Use socket to communicate with this particular client only, sending it it's own id
socket.emit('welcome', { message: 'Welcome!', id: socket.id });
socket.on('i am client', console.log);
});
app.listen(3000);

Socket IO server neither emits nor receive any message to/from client : MEAN with ionic

I am writing a chat app using MEAN stack, however i got stuck while implementing them. The server is not getting any kind of update from client neither it sends any thing to client. I guess there is some error in server, but could not figure it out any where.
The server.js file is :-
(function (exports, require, module, __filename, __dirname) {
var express = require('express'),
neo4j = require('node-neo4j'),
bodyParser = require('body-parser'),
app = express();
//socket io integration
var io = require('socket.io')(3000);
var clients = [];
app.get('/', function (req, res) {
res.sendfile('./index.html');
});
io.on('connection', function (socket)
{
console.log("Server : Connected to Socket IO");
console.info('New client connected (id=' + socket.id + ').');
socket.on('user enter',function(user_name){
clients.push({id:socket.id,userName:user_name});
len=clients.length;
io.emit('user entrance',clients,clients[len-1].id);
});
socket.on('send msg',function(data){
socket.broadcast.to(data.id).emit('get msg',{msg:data.msg,id:data.id,name:data.name});
});
socket.on('disconnect', function(data){
console.log("user disconnected :" +data.id);
})
});
app.use(require("express").static('data'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.set('port', process.env.PORT || 5000);
app.listen(app.get('port'), function () {
applogger.info('Express server listening on port ' + app.get('port'));
});
});
The client side code (chatController.js) is -
angular.module('myApp')
.controller('ChatCtrl',function($rootScope, $scope, $stateParams,socket,$ionicScrollDelegate,$timeout) {
$scope.userName = $scope.user.name;
socket.on('connect',function()
{
console.log("from userListCtrl : "+ JSON.stringify($rootScope.selectedUser));
$scope.selected_id=$rootScope.selectedUser;
});
socket.emit('user enter',$scope.userName);
socket.on('user entrance',function(data,my_id){
//checking the user id
if($scope.my_id==null){
$scope.my_id=my_id;
}
$scope.user_show=data;
});
$scope.send_msg = function($event){
var keyCode = $event.which || $event.keyCode;
if($scope.selected_id==$scope.my_id){
alert("You can't send msg to your self.");
}else{
if (keyCode === 13) {
var data={
id:$scope.selected_id,
msg:$scope.msg_text,
name:$scope.userName
};
$scope.msg_text='';
socket.emit('send msg',data);
}
}
};
//Getting the messages from server.
socket.on('get msg',function(data){
$scope.msgs=data;
$scope.is_msg_show=true;
addMessage();
});
// function of push messages to an array.
The UI part is very clean, it's user list on which you can click and it will open a chat window where these two user can chat personally. Every thing is happening in client side (such as the user details, msg content etc) except sending the msg to server and getting response from them.
I am stuck in this.

How do I render a new jade view upon a socket event?

I have two major js files, one on the server side which is the server.js and another on the client side, which is enterchat.js. These two files are the ones which will communicate via socket.io. All socket events are working as expected.
server.js
var express = require('express'),
...
server = require('http').createServer(app),
io = require('socket.io').listen(server);
var usernames = [],
username_sockets = [];
...
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use('/', express.static(__dirname+'/public/'));
app.get('/chat', function (req, res) {
res.render('checkUsername', {title:'Socket IO Chat'});
});
app.get('/chatwindow', function (req, res) {
res.render('chatwindow', {title:'Welcome to chat window'});
});
io.sockets.on('connection', function (socket) {
socket.on('disconnect', function () {
...
delete username_sockets[socket.id];
console.log("Disconnected from " + user);
});
socket.on('newusr', function (newusrname) {
console.log("New user name request:: " + newusrname);
if(usernames.indexOf(newusrname) >= 0)
{
console.log("Already used username..");
socket.emit('usernameTaken', newusrname);
}
else
{
socket.emit('usernameavlbl', newusrname);
}
});
socket.on('startchat', function (usernameAvailable) {
if(usernames.indexOf(usernameAvailable) >= 0)
{
console.log("Just taken username..");
socket.emit('usernameJustTaken', usernameAvailable); //returning the username that was just taken
}
else
{
usernames.push(usernameAvailable);
console.log("Opening chat window for "+usernameAvailable);
username_sockets[socket.id] = usernameAvailable;
// trying to render jade view to open chatwindow on socket event
}
});
socket.on('sndmsg', function (message) {
socket.broadcast.emit('msgreceive', message, username_sockets[socket.id]);
});
socket.on('typing', function (username) {
socket.broadcast.emit('usertyping', username);
});
socket.on('stoppedtyping', function (username) {
socket.broadcast.emit('userstoppedtyping', username);
});
});
server.listen(8080,'0.0.0.0');
console.log("Listening on 8080..");
enterchat.js
var socket, usernameAvailable;
$(document).ready(function () {
connect();
...
...
$('#checkBtn').on('click', function(event) {
if($('#username').val() == '')
alert("Choose a username");
else
{
var newusrname = $('#username').val();
socket.emit('newusr', newusrname);
}
});
...
socket.on('usernameTaken', function (message) {
alert(message + " is already taken. Try another one..");
});
socket.on('usernameJustTaken', function (message) {
alert(message + " was just taken. Try another one..");
});
socket.on('usernameavlbl', function (newusrname) {
$('#chataway').attr('disabled', false);
usernameAvailable = newusrname;
});
$('#chataway').on('click', function () {
socket.emit('startchat', usernameAvailable);
});
});
function connect () {
socket = io.connect(null);
}
My question: How do I render the chatwindow view upon the socket event startchat?
I looked at this question: In Express.js, how can I render a Jade partial-view without a "response" object?, but I am not sure as to how to add it in my code so that a fresh jade view (chatwindow) is loaded on the browser.
You can use compileFile method of jade api, get the html and then emit a socket event containing the html data. You can append that html to the DOM.
socket.on('startchat', function (usernameAvailable) {
if(usernames.indexOf(usernameAvailable) >= 0)
{
console.log("Just taken username..");
socket.emit('usernameJustTaken', usernameAvailable); //returning the username that was just taken
}
else
{
usernames.push(usernameAvailable);
console.log("Opening chat window for "+usernameAvailable);
username_sockets[socket.id] = usernameAvailable;
var fn = jade.compileFile('path to jade file', options);
// Render function
var html = fn();
// Now you can send this html to the client by emitting a socket event
}
});

Categories

Resources