If this is a vague question, please let me know, so that I can specify. I really want to get around this stump.
I have read several cheat sheets regarding how to properly broadcast a message to a client room. My reference is: https://github.com/socketio/socket.io/blob/master/docs/emit.md.
To describe my problem:
The chat page comes up with the send button correctly, however as soon as I click send, nothing is ever sent.
The interesting thing, is that whenever I am not using rooms, and just use the default namespace, I can get messages.
Any idea on what is going on? Thank you!!
server.js
io.on('connection', function(socket) {
global.rooms = 'room1';
socket.on('subscribe', function(room) {
socket.join(rooms);
console.log(socket.id + 'joining room', rooms)
})
socket.on('chat', function(data) {
io.to(rooms).emit('chat', data);
})
})
chat.jade
extends layout
block content
h2.page-header Chat Page
div(id='the-chat')
div(id='chat-window')
div(id='output')
input(id='handle', type='text', value = user.name, style= 'width: 0px; visibility: hidden;')
input(id='message', type='text', placeholder='message')
button(id='send' value='Send') Send
//imports the socket.io functionality on the client side for the chat.jade application
script(src="/socket.io/socket.io.js")
script.
//variable created that mirrors connection made in the backend
//matches the connection made in the server side
var socket = io.connect('http://localhost:3000')
//Query dom
var message = document.getElementById('message')
var handle = document.getElementById('handle')
var btn = document.getElementById('send')
var output = document.getElementById('output')
//emit events
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
socket.on('chat', function(data) {
output.innerHTML += '<p><strong>' + data.handle + ': </strong>' + data.message + '</p>';
document.getElementById('message').value = "";
})
Home.jade
extends layout
block content
h2.page-header(style = "text-align: center;").
Home Page
if (user.isTutor)
b(style = "text-align: center;")
form(method='post', action = '/home/available')
input.btn.btn-primary(type = 'submit',name='isAvailable', value = 'Available', id = 'button4')
form(method='post', action = '/home/unavailable')
input.btn.btn-primary(type = 'submit',name='isUnavailable', value = 'Unavailable', id = 'button5')
script(src="/socket.io/socket.io.js")
script.
var btn = document.getElementById('button4')
//var space = '#{user.room}'
var socket = io.connect()
btn.addEventListener('click', function() {
socket.emit('subscribe', 'room1')
})
div(id = 'magic')
form(method='get')
if (user.hasApplied)
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/findatutor';" name='find', value = 'Find a Tutor', class = 'middle', id = 'button7')
else if (user.hasApplied == false)
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/findatutor';" name='find', value = 'Find a Tutor', id = 'button1')
input.btn.btn-primary(type = 'submit', onclick = "javascript: form.action = '/apply';" name='become', value = 'Become a Tutor', id = 'button2')
Home.js
router.post('/available', ensureAuthenticated, (req,res,next) => {
var io = res.locals['socketio']
db.collection('DefaultUser').update({_id: req.user._id}, {$set: {isAvailable: true}});
res.redirect('../chat')
})
The problem is you did not specify where to render the messages. As I understood, you have no problem creating rooms, so I will explain step by step how to handle after that point.
According to your code this is the communication between server and client for sending messages
//server.js
socket.on('chat', function(data) {
io.to(rooms).emit('chat', data);
})
//chat.jade
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
First client is sending the message (typing the input box) to the server and when server receives it, it has to send the same message to all other clients. once clients receive this message, clients have to figure out how to display it. But when server sends the received message, then you have to initiate a new event. let's call it display. so your code should be like this:
//server.js
//i always use arrow functions, but I will follow along your code
socket.on('chat', function(data) {
io.to(rooms).emit('display', data);
})
Now your client should be listening for this event and should be handling where to display it:
//chat.jade
socket.on('display', (data) => {
const displayedMessage = pug.render(messageTemplate, {
message: data.message,
})
$messages.insertAdjacentHTML('beforeend', displayedMessage)
})
Since Jade has been renamed to pug, i used pug here. So i will render {message: data.message} into html in a script tag then placed it into DOM ELEMENT $message.
I am not sure where you want to render handle:handle.value i will just show how to display message. similarly, you can handle it.
Now what are messageTemplate, $messages and insertAdjacentHTML()?
create a div tag and a script tag in your html
//this is where you are gonna display the message
<div id="messages" class="chat__messages"></div>
<!-- template messages -->
<script id="message-template" type="text/html">
<div>
<p>{{message}}</p>
</div>
</script>
//chat.jade
const $messages = document.getElementById("messages");
const messageTemplate = document.getElementById("message-template").innerHTML;
The insertAdjacentHTML() method inserts a text as HTML, into a specified position. you can get more explanation and examples here:
https://www.w3schools.com/jsref/met_node_insertadjacenthtml.asp
socket io code looks long and complicated but if you know the logic and move step by step it will easy to implement it.
Try
io.in(rooms).emit('chat', data);
Missing this under global.rooms = 'room1';
var rooms = global.rooms
Longer Answer: At least what I can see, you've added "rooms" as a property on the global object (idk where 'global' itself is defined, but if it's not throwing an error, I'm assuming you defined it above). While it's a property, the variable 'rooms' that you're using as a namespace isn't defined at the time you're calling it, so it doesn't know where to emit the message.
Even more answer: Also, if you're intending to add additional rooms to global.rooms, I think you might want to use a hashlist to store them, so that you can easily access them as global.rooms[room], as well as easily add new rooms to the list ie global.rooms[room.name] = room
Related
I'm trying to build a system for like & dislike posts in my project but I'm facing some issue using this on the client side , I have this form
but the problem there is no communication between server and client I don't know what I'm missing, even I click on like button I see in the client side value 1 but nothing happen on server side
the front is build with EJS view engine and this my code
<div class="row">
<button onclick="actOnPost(event);"
data-post-id="<%= user.posts[x].id %>">Like
</button>
<span id="likes-count-<%= user.posts[x].id %>"><%= user.posts[x].likes %></span>
</div>
I use this script in the index.ejs file :
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var updatePostStats = {
Like: function (postId) {
document.querySelector('#likes-count-' + postId).textContent++;
},
Unlike: function(postId) {
document.querySelector('#likes-count-' + postId).textContent--;
}
};
var toggleButtonText = {
Like: function(button) {
button.textContent = "Unlike";
},
Unlike: function(button) {
button.textContent = "Like";
}
};
var actOnPost = function (event) {
var postId = event.target.dataset.postId;
var action = event.target.textContent.trim();
toggleButtonText[action](event.target);
updatePostStats[action](postId);
axios.post('/posts/' + postId + '/act', { action: action });
};
</script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script>
var pusher = new Pusher('your-app-id', {
cluster: 'your-app-cluster'
});
var socketId;
// retrieve the socket ID on successful connection
pusher.connection.bind('connected', function() {
socketId = pusher.connection.socket_id;
});
var channel = pusher.subscribe('post-events');
channel.bind('postAction', function(data) {
// log message data to console - for debugging purposes
console.log(data);
var action = data.action;
updatePostStats[action](data.postId);
});
</script>
for the server side I have this code located in file.js:
router.post('/posts/:id/act', (req, res, next) => {
console.log('im here')
const action = req.body.action;
const counter = action === 'Like' ? 1 : -1;
Post.update({_id: req.params.id}, {$inc: {likes_count: counter}}, {}, (err, numberAffected) => {
pusher.trigger('post-events', 'postAction', { action: action, postId: req.params.id }, req.body.socketId);
res.send('');
});
});
I added console.log('Im here') to verify if there something is launching on my server side but I don't get anything, this router is not even launched
my mongodb image for posts and like button is below :
could it be possible to help on solving this or provide me a better exemple to follow ?
Best Regards,
Instead of making string or number in like, make an array, push req.user.id- user-id of the particular logged in user, and get the array length. This will give you total number of likes. In order to get toggle functionality, search that user id using findOne method and if no user found, push the id of the user who clicked the like button else pull the user id of the same user. Always print the length of the array so that while performing push or pull operations, you will get real time length of the like array.
I got a very simple socket.io application where I'm able to see when a user connects to my site and when a user leaves. The first user is able to see everybody that gets in, however the second will only see the people coming in after him, and so on.... What I want the user that connects to see are all current connected users
Server.js
var io = require("socket.io")(http);
var users = [];
io.on("connection", function (socket) {
console.log("User connected", socket.id);
Some loop to see user connected I think?!
socket.on("user_connected", function (username) {
users[username] = socket.id;
console.log(users);
if (username !== 'null'){
io.emit("user_connected", username);
}
});
socket.on("send_message", function (data) {
io.emit("new_message", data);
});
socket.on("user_left", function (datan) {
io.emit("user_remove", datan);
console.log("user left", datan);
});
});
site.html
window.onbeforeunload = function () {
io.emit("user_left", name);
};
var name = <?php echo json_encode($_SESSION['displayname']); ?>;
var bild = <?php echo json_encode($_SESSION['userpic']);?>;
var dt = new Date();
var tid = dt.getHours() + ":" + dt.getMinutes();
io.emit("user_connected", name);
sender = name;
io.on("user_connected", function (username) {
var html = "";
html += "<li data-username='" + username + "'>" + username + "</li>";
document.getElementById("users").innerHTML += html;
});
Looked into this question, but me a dumb coder doesn't understand what 'my_room' is towards my soloution. Sorry for weird question, I'm just kind of lost
If you want to send a user list to everyone, create an object like users, add a record each time a user connected, and broadcast to everyone.
if someone disconnects, then first remove that from your collection then send only disconnected user's info to clients, so clients can delete that user from their list.
If all you want is total number of questions, just send io.sockets.clients().length
I want to send an email notification whenever the user clicks on a button. The button will call sendEmail(widget) function and invoke a client script as follow:
function sendEmail(widget){
var item = widget.datasource.item;
google.script.run
.withFailureHandler(function(error) {
console.text = error.message;
})
.withSuccessHandler(function(result) {
console.text = 'succeed';
})
.sendEmails(item);
}
then it will pass the datasource on item and call sendEmails(item) function from a server script as follows:
function sendEmails(item){
var to = item.OwnerEmail;
var subject = 'Please Review';
var body = 'hello<br/>my name is Muhammad Alif bin Azali';
MailApp.sendEmail({
to: to,
subject: subject,
htmlBody: body,
noReply: true
});
}
but when I click the button I got following error instead. Any help?
Unfortunately, you cannot pass whatever you want as a parameter to your server function. Communication to the server has some limitations:
...most types are legal, but not Date, Function, or DOM element...
...objects that create circular references will also fail...
App Maker's records definitely violate those restrictions.
There are different strategies to handle this limitation, one of them is passing record's key as function's parameter.
// Client script
function sendEmail(widget) {
var item = widget.datasource.item;
google.script.run
...
.sendEmails(item._key);
}
// Server script
function sendEmails(itemKey) {
var item = app.models.MyModel.getRecord(itemKey);
...
}
Looks like your Mail Body needs to be converted in HTML Body,
Here's the sample
function sendEmails(item){
var to = item.OwnerEmail;
var subject = 'Please Review';
var body = 'hello<br/>my name is Muhammad Alif bin Azali';
var template = HtmlService.createTemplate(body);
var htmlBody = template.evaluate().getContent();
MailApp.sendEmail({
to: to,
subject: subject,
htmlBody: htmlBody ,
noReply: true
});
}
I have tried several variations of trying to emit to all users connected to a particular /namespace, but have had no luck. I could be misunderstanding how sockets work.
But what I have right now is two browsers open on different pages. When a user connects to pageA, that user is now part of '/users' namespace. When a user connects to pageB, that user is now part of '/valets' namespace.
I have a .emit() on pageA that sends to server.js. I listen for it with .on(), and then try to run .emit() but to only the users in '/valets' namespace.
I am able to see in my terminal "listening for request valet" and the console.log(data) part.
I believe my problem is the usr_nsp.of('/valets').emit("incoming-request",{data:data}); portion. The other commented lines are what I have tried so far. They all give me an error: is not a function.
server.js
var app = require('http').createServer();
var io = require('socket.io')(app);
app.listen(3000, function(){
console.log('listening on port 3000');
});
var redis = require('socket.io/node_modules/redis');
// create custom namespace for Users
var room_number;
var usr_nsp = io.of('/users');
usr_nsp.on('connection', function(socket){
console.log('user has connected to /users namespace');
socket.on('request-valet', function(data){
console.log("listening for request valet");
console.log(data);
room_number = data.room_number;
socket.join(room_number);
// usr_nsp.broadcast.of('/valets').emit("incoming-request",{repark:data});
// usr_nsp.of('/valets').broadcast.emit("incoming-request",{repark:data});
// io.of('/valets').emit("incoming-request",{repark:data});
// socket.of('/valets').emit("incoming-request",{repark:data});
usr_nsp.of('/valets').emit("incoming-request",{repark:data});
});
});
var valet_nsp = io.of('/valets');
valet_nsp.on('connection', function(socket){
console.log('valet has connected to /valets namespace');
// var room_number;
socket.on('join-room', function(data){
// assign valet to room
room_number = data.room_number;
socket.join(room_number);
//valet_nsp.sockets.in(room_number).emit("request-accepted",{current_pos:current_pos})
});
socket.on('set-valet-starting-position', function(data){
//var valet_starting_pos = data.starting_position;
valet_nsp.sockets.in(room_number).emit('activate-directions-service', {repark:data});
})
socket.on('get-new-location', function(data){
// send the updated location only to User
// maybe use .broadcast??
valet_nsp.sockets.in(room_number).emit("update-valet-location", {current_pos:data});
});
});
pageB.html (sockets portion)
socket.on('incoming-request', function(data){
console.log("incoming request");
alert("incoming request");
// use data to display on html screen
});
The namespace handle you created is used to emit to users in that particular namespace. This should thus work:
var users = io.of('/users'),
valets = io.of('/valets');
users.on('connection', function(socket) {
socket.on('request-valet', function(data) {
valets.emit('incoming-request', { repark : data });
});
});
I've created client and server of node with socket.io. server is executing 4 get requests of news feed and fetched the data. These data is sent to the client with socket.io.
client is displaying news feed on the occurrence of specific socket.io event.
This works well for once. Here is the code and working fiddle
server.js
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
, redis = require("redis");
var http = require("http");
// initialize the container for our data
var data = "";
var nfs = [
"http://economictimes.feedsportal.com/c/33041/f/534037/index.rss",
"http://www.timesonline.co.uk/tol/feeds/rss/uknews.xml",
"http://www.independent.co.uk/news/business/rss",
"http://www.dailymail.co.uk/money/index.rss"
];
//setInterval(function() {
for(var i=0; i<nfs.length; i++){
//console.log(nfs[i]);
http.get(nfs[i], function (http_res) {
// this event fires many times, each time collecting another piece of the response
http_res.on("data", function (chunk) {
// append this chunk to our growing `data` var
data += chunk;
});
// this event fires *one* time, after all the `data` events/chunks have been gathered
http_res.on("end", function () {
// you can use res.send instead of console.log to output via express
console.log("data received");
});
});
}
//}, 30000);
app.listen(8080);
function handler (req, res) {
fs.readFile(__dirname + '/client.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) {
//setInterval(function() {
socket.emit('news', data);
/*socket.on('my other event', function (data) {
console.log(data);
});*/
//}, 5000);
});
client.html
<html>
<head>
<script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
//socket io client
var socket = io.connect('http://localhost:8080');
//on connetion, updates connection state and sends subscribe request
socket.on('connect', function(data){
setStatus('connected');
socket.emit('subscribe', {channel:'notif'});
});
//when reconnection is attempted, updates status
socket.on('reconnecting', function(data){
setStatus('reconnecting');
});
//on new message adds a new message to display
socket.on('news', function (data) {
console.log(data);
//socket.emit('my other event', { my: 'data' });
addMessage(data);
});
/*socket.on('news', function (data) {
debugger;
socket.emit('my other event', { my: 'data' }
var msg = "";
if (data) {
msg = data;
}
addMessage(msg);
});*/
//updates status to the status div
function setStatus(msg) {
$('#status').html('Connection Status : ' + msg);
}
//adds message to messages div
function addMessage(msg) {
//debugger;
var $xml = $(msg);
var html = '';
$xml.find("item").each(function() {
var $item = $(this);
html += '<li>' +
'<h3><a href ="' + $item.find("link").text() + '" target="_new">' +
$item.find("title").text() + '</a></h3> ' +
'<p>' + $item.find("description").text() + '</p>' +
// '<p>' + $item.attr("c:date") + '</p>' +
'</li>';
});
$('#result').prepend(html);
}
</script>
</head>
<body>
<div id="status"></div><br><br>
<ul id="result"></ul>
</body>
</html>
What I understand about socket.io is that we don't need long server polling and so how do server come to know that news is added to the respected news feed.
How do I update the client with newly added news when news is added to the news feed rss ???
Update
Ok so from all the responses I get the point that it is not possible for socket.io to know that new entry has been added. So, how do I know (which tools/libraries do require to know that new entry has beed added and update the client as well) ???
Retrieving the messages from the news feeds are completely independent of socket.io unless the news feeds implement sockets on their end and your server becomes their client. So you will have to continue to poll them with http requests to know whether they have updated data.
In order to notify your clients of the update you would just emit the news event. Presumably you would have logic on the server to make sure you are only sending events which have not previously be sent.
There is no way for "node" to know when a new entry is added to the news feed. You will have to poll the news service like you are doing now. This really has nothing to do with Node or Socket.io unless I completely misunderstand what you are asking.