I have problem with socket.io. In my code router.post(/comment,...) saving user comments in database (using mongoose) and I am trying emit this save. In controller function readMoreCourse is to get and display all comments from database (and question how use socket to this function that using ng-reapat display comment in real-time). Function AddComment is on client side chceck valid form and next post comment to database.
My question: How in real-time save and display user comment using angular (ng-repeat?) and socket.io? Honestly I making this first time, and I have short time, thanks for any help.
Server
io.on('connection', function(socket){
socket.emit('comment', function(){
console.log('Comment emitted')
})
socket.on('disconnect', function(){
})
})
API
router.post('/comment', function(req, res) {
Product.findOne({ _id: req.body._id }, function(err, product){
if(err) {
res.json({ success:false, message: 'Course not found' })
} else {
User.findOne({ username: req.decoded.username }, function(err, user){
if(err){
res.json({ success:false, message: 'Error'})
} else {
product.comments.push({
body: req.body.comment,
author: user.username,
date: new Date(),
});
product.save(function(err){
if(err) throw err
res.json({ success: true, message: 'Comment added })
**io.emit('comment', msg);**
})
}
})
}
})
})
controller
Socket.connect();
User.readMoreCourse($routeParams.id).then(function(data){
if(data.data.success){
app.comments = data.data.product.comments;
} else {
$window.location.assign('/404');
}
});
app.AddComment = function(comment, valid) {
if(valid){
var userComment = {};
userComment.comment = app.comment;
Socket.on('comment', User.postComment(userComment).then(function(data){
if(data.data.success){
$timeout(function(){
$scope.seeMore.comment = '';
},2000)
} else {
app.errorMsg = data.data.message;
}
}));
} else {
app.errorMsg = 'Error';
}
}
$scope.$on('$locationChangeStart', function(event){
Socket.disconnect(true);
})
factory
userFactory.readMoreCourse = function(id) {
return $http.get('/api/seeMore/' + id)
}
userFactory.postComment = function(comment){
return $http.post('/api/comment', comment);
}
.factory('Socket', function(socketFactory){
return socketFactory()
})
In your socket factory, initialize socket.io emit and on events.
app.factory('socket', ['$rootScope', function($rootScope) {
var socket = io.connect();
return {
on: function(eventName, callback){
socket.on(eventName, callback);
},
emit: function(eventName, data) {
socket.emit(eventName, data);
}
};
}]);
and call this from controller
app.controller('yourController', function($scope, socket) {
User.postComment(userComment).then(function(data){
if(data.data.success){
$timeout(function(){
$scope.seeMore.comment = '';
},2000);
// Emit new comment to socket.io server
socket.emit("new comment", userComment);
} else {
app.errorMsg = data.data.message;
}
});
// other clients will listen to new events here
socket.on('newComment', function(data) {
console.log(data);
// push the data.comments to your $scope.comments
});
from socket.io server
io.on('connection', function(socket) {
// listen for new comments from controller and emit it to other clients
socket.on('new comment', function(data) {
io.emit('newComment', {
comment: data
});
});
});
EDIT:
If you just want to push from server side,
io.on('connection', function(socket) {
// after saving your comment to database emit it to all clients
io.emit('newComment', {
comment: data
});
});
and remove this emit code from controller:
socket.emit("new comment", userComment);
But this method can be tricky because the user who posts the comment should immediately see the comment added to the post. If you let socket.io to handle this there will be a few seconds lag for the guy who posted the comment.
Related
I am using ionic framework for my android app and MEANJS on my server. I am using Web Sockets to get realtime data. While the server side web application updates automatically every time a CRUD happens in the android application, the android app does not update automatically when a change is made on the server side.
Android App Service(AngularJS)
.service('Socket', ['Authentication', '$state', '$timeout',
function (Authentication, $state, $timeout) {
// Connect to Socket.io server
this.connect = function () {
// Connect only when authenticated
if (Authentication.user) {
this.socket = io('https://cryptic-savannah-60962.herokuapp.com');
}
};
this.connect();
// Wrap the Socket.io 'on' method
this.on = function (eventName, callback) {
if (this.socket) {
this.socket.on(eventName, function (data) {
$timeout(function () {
callback(data);
});
});
}
};
// Wrap the Socket.io 'emit' method
this.emit = function (eventName, data) {
if (this.socket) {
this.socket.emit(eventName, data);
}
};
// Wrap the Socket.io 'removeListener' method
this.removeListener = function (eventName) {
if (this.socket) {
this.socket.removeListener(eventName);
}
};
}
Client Side Controller
if (!Socket.socket && Authentication.user) {
Socket.connect();
}
Socket.on('orderCreateError', function (response) {
$scope.error = response.message;
});
Socket.on('orderCreateSuccess', function (response) {
if ($scope.orders) {
$scope.orders.unshift(response.data);
}
});
Socket.on('orderUpdateSuccess', function (response) {
if ($scope.orders) {
// not the most elegant way to reload the data, but hey :)
$scope.orders = Orders.query();
}
});
Server Controller(NodeJS)
socket.on('orderUpdate', function (data) {
var user = socket.request.user;
// Find the Order to update
Order.findById(data._id).populate('user', 'displayName').exec(function (err, order) {
if (err) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else if (!order) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: 'No order with that identifier has been found' });
} else {
order.name = data.name;
order.phone = data.phone;
order.water = data.water;
order.waiter = data.waiter;
order.napkin = data.napkin;
order.complete = data.complete;
order.rating = data.rating;
order.payment_mode = data.payment_mode;
order.order_source = data.order_source;
order.orderfood = data.orderfood;
order.save(function (err) {
if (err) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else {
// Emit a success response event
io.sockets.emit('orderUpdateSuccess', { data: order, updatedBy: user.displayName, updatedAt: new Date(Date.now()).toLocaleString(), message: 'Updated' });
}
});
}
});
});
You have two emit channels on your server side but neither event is handled on the client side.
Per socket.io docs, you need something like:
socket.on('orderUpdateSuccess', function (data) {
// do something inside the app that will update the view
console.log(data);
Orders.update(data); // assuming you have a service called Orders to keep track of live data -- don't forget [$scope.$apply][2]
});
Using your example code
Socket.on('orderUpdateSuccess', function (response) {
if ($scope.orders) {
$scope.$apply(function() {
// not the most elegant way to reload the data, but hey :)
$scope.orders = Orders.query();
});
}
});
I developed a filter checkbox to pass the parameter to "filter results only with photo".
If I put the code below directly on the server works, but via angular goes wrong.
Is there another way to do this?
$scope.query = {
picture: { $exists: true }
};
My server router:
app.get('/api/users', ensureAuthenticated, function(req, res) {
console.log(req.query);
User.find(req.query, function(err, users) {
if (err) {
res.send(err);
}
res.json(users);
})
.sort({ rating: -1 });
});
And my controller checkbox:
<md-checkbox class="md-primary" ng-model="query.picture" ng-change="loadusers()">filter results only with photo</md-checkbox>
My function retrieve server
$scope.loadusers = function(){
$ionicLoading.show({
template: 'Carregando...'
});
diaristajaAPI.all('users').getList($scope.query)
.then(function(users){
$ionicLoading.hide();
$scope.listusers = users;
})
.catch(function(error) {
$ionicLoading.hide();
if (error.error) {
// Popup error - invalid redirect_uri, pressed cancel button, etc.
$mdToast.showSimple(error.error);
} else if (error.data) {
// HTTP response error from server
$mdToast.showSimple(error.data.message, error.status);
} else {
$mdToast.showSimple(error);
}
});
};
Use a boolean for the checkbox.
$scope.query = {picture: true};
Then on the server it's simple to just do
req.query.picture = {$exists: !!req.query.picture};
I created a chat app using socket.io, everything works very well except for when one of the users refresh. For instance:
If user A joins room 1, and user B joins room 1, they can communicate smoothly. If user A refreshes his browser, he can still send messages to user B in real time, but user B no longer sends user A messages in real time.
For some reason, when a user refreshes the page, he is no longer able to receive messages in real time. Here is how I have setup my chat system:
server side:
server.listen(port);
var io = require('socket.io').listen(server);
io.on('connection', function(socket){
console.log("User Connected");
socket.on('comment', function(data){
//socket.broadcast.emit('comment', data);
console.log('server sidecomment');
socket.broadcast.to(data.chatId).emit('comment',data);
});
socket.on('disconnect', function(){
console.log('user disconnected');
});
socket.on('join:room', function(data){
socket.join(data.chatId);
socket.broadcast.to(data.chatId).emit('join:room', data)
console.log('joined room: ' + data.chatId);
});
socket.on('leave:room', function(data){
socket.leave(data.chatId);
console.log('left room: ' + data.chatId);
})
socket.on('typing', function(data){
console.log('server side start typing');
console.log(data);
socket.broadcast.to(data.chatId).emit('typing',data);
});
socket.on('stoptyping', function(data){
console.log('server side stop typing');
socket.broadcast.to(data.chatId).emit('stoptyping');
});
});
client side:
$scope.$on("$destroy", function() {
console.log('leaving page');
socket.emit('leave:room', {
'chatId': post.chatId
});
var leaveMessage = auth.currentUser() + ' has left';
posts.addComment(post._id, {
body: leaveMessage,
author: 'server'
}).success(function(comment) {
$scope.post.comments.push(comment);
});
});
socket.on('comment', function(data) {
console.log('client side comment');
$scope.post.comments.push(data);
});
socket.on('join:room', function(data) {
var joinedMessage = {
body: data.author + ' has joined',
author: 'server'
};
$scope.post.comments.push(joinedMessage);
});
$scope.whenTyping = function() {
socket.emit('typing', {
'chatId': post.chatId,
'author': auth.currentUser()
});
}
$scope.notTyping = function() {
socket.emit('stoptyping', {
'chatId': post.chatId
});
}
socket.on('typing', function(data) {
$scope.typing = data.author + ' is typing';
});
socket.on('stoptyping', function() {
$scope.typing = '';
});
My question is: Is the socket being timed out? If it is, is there a way I can prevent it from being timed out?
Im trying to publishUpdate some data from the server in order to listen for user/profile creation but im not able to listen to those events in the client using Angular. Is it possible im missing something here? Or maybe something wrong im doing?
// UserController
saveUser: function(req, res) {
User.create({name: req.param('name'), profile: {aboutMe: req.param('about'), gender: req.param('gender')}})
.exec(function(err, user) {
Profile
.findOneById(user.profile)
.exec(function(err, profile) {
profile.user = user.id;
profile.save(function(error, saved){
if (error) return res.badRequest('Error.');
if (saved) res.json(saved);
Profile.publishUpdate(saved.id, saved);
});
});
});
}
// Client Angular
$scope.send = createUser;
listenProfile();
function createUser() {
var obj = {
name: $scope.name,
about: $scope.profile.about,
gender: $scope.profile.gender
User.create(obj).then(function(data) {
console.log(data); // Data displayed correctly
}, function(error) {
console.log(error);
});
}
function listenProfile() {
io.socket.on('profile',function(data){
console.log(data); //Nothing happens?
});
};
Try changing it to a capital P:
function listenProfile() {
io.socket.on('Profile',function(data){
console.log(data); //Nothing happens?
});
};
I think that are missing "suscribe" to your model... for example
Profile.subscribe(req.socket, data);
And linked to your app via GET.
io.socket.get("/profile",function(data){
console.log(data);
});
I am using mongoose to create a user object on register. This works fine and any errors are returned as expected.
However, I want to log the user on right after they register (so registering logs you on if there are no errors).
I have the following for the register.
register_controller:
$scope.submitRegister = function() {
AuthenticationService.register(this.details).success(function() {
$log.debug('/POST to /api/register worked');
});
}
services.js:
.service('AuthenticationService', function($http, $timeout, $q, $session, $flash) {
...
this.register = function(details) {
var register = $http.post('/api/register', details);
register.success(function() {
console.log("User added fine");
}).error(function() {
console.log("error!!!");
});
return register;
};
...
users.js:
app.post('/api/register', authentication.register);
passport's authenticate.js:
module.exports = {
...
register: function(req, res){
var User = require('./controllers/api/login_api');
User.create({name: req.body.name, email: req.body.email, password: req.body.password}, function(err){
if (err) {
console.log(err);
return;
}
console.log("User added");
return res.send(200);
});
},
...
The error is reported back fine, no troubles, but I would have thought it would report something else back (like the created object?) which I could use down the line so my register_controller can have in the success function(object) {... login(object);...}.
Is this a limitation in the .create method or am I missing something obvious?
Thank you.
Two things to change in your server code:
passport's authenticate.js:
module.exports = {
...
register: function(req, res){
var User = require('./controllers/api/login_api');
User.create({name: req.body.name, email: req.body.email, password: req.body.password}, function(err, user){
if (err) {
console.log(err);
return;
}
console.log("User added");
return res.send(200, user);
});
},
...
I've added user to your callback Mongoose model.create api - return the created object to the CB
And to change the catch in your client:
.service('AuthenticationService', function($http, $timeout, $q, $session, $flash) {
...
this.register = function(details) {
var register = $http.post('/api/register', details);
register.success(function(user) {
console.log(user);
}).error(function() {
console.log("error!!!");
});
return register;
};
...
Now you can do with the created user object whatever you need