I'm using Sails JS and i tried to use socket IO librarie.
As it is explained in Sails JS documentation : http://sailsjs.org/#!documentation/sockets
I create a controller with this code:
index: function(req, res) {
var param = req.param('msg');
console.log("PARAM : ", param); // For display the message in terminal
// Send a JSON response
return res.json({
success: true,
message: param
});
},
And in my template (i use swig JS template engine) :
For include socket io library :
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
And in JavaScript code :
var socket = io.connect('http://localhost:1337');
socket.get('/echo', {
message: 'hi there!'
}, function(response) {
console.log ('response : ', response);
});
And i've this error : undefined is not a function on socket.get('/echo' ...
Thanks for your help !
I believe you need to add <script type="text/javascript" src="/app/sails.io.js"></script> as well. Since Socket.prototype.get is defined in this file.
Related
I am new to the technologies NodeJS and Socket.io and i am facing a problem with the variables stored in io.sockets.adapter.rooms.
I have my app.js :
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('message', function (room) {
socket.join(room);
console.log(io.sockets.adapter.rooms);
io.sockets.adapter.rooms[room].variable = "This is a test";
})
});
http.listen(3000, () => {
console.log('listening on *:3000');
});
The index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<input type="button" value="Press me" id="test">
<body>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
document.getElementById('test').addEventListener('click', function () {
socket.emit('message', 'room1');
})
</script>
</body>
</html>
And the server return this error :
TypeError: Cannot set property 'variable' of undefined
It means that "io.sockets.adapter.rooms[room].variable" is undefined but i do not understant how i can define it otherwise.
By the way I am using NodeJS 12.8.3 and express 4.17.1
Maybe I need a specific module ?
Thanks a lot to everyone who will take the time to answer this
In their changelog for v3.0.0 they mention the following
Socket#rooms is now a Set instead of an object
Which means it's now a Map object instead of a normal js object
So with v3.0.3 you get the following output when logging the rooms, which you can't access by key like a normal js object, and .set() has to be used instead.
Map(2) {
'II-OqYrpZYtv2bKrAAAD' => Set(1) { 'II-OqYrpZYtv2bKrAAAD' },
'room1' => Set(1) { 'II-OqYrpZYtv2bKrAAAD' }
}
Although I have no idea how to add an additional variable to the room. Since if you run the following code:
io.sockets.adapter.rooms.set(room, {
variable: "Test variable"
})
it replaces the socket id with the new variable. I can't find anything on how they want us to store variables using this new structure.
Map(2) {
'Xv6vbxuOgDuqp6dmAAAB' => Set(1) { 'Xv6vbxuOgDuqp6dmAAAB' },
'room1' => { variable: 'Test variable' }
}
When logging the rooms with v2.0.3 you get a normal object output which you can access with [key].
{
'tVnQpZyKDglSf4J-AAAA': Room { sockets: { 'tVnQpZyKDglSf4J-AAAA': true }, length: 1 },
room1: Room { sockets: { 'tVnQpZyKDglSf4J-AAAA': true }, length: 1 }
}
So if you revert socket.io back to v2.0.3 (the latest 2.x.x version) your code works fine.
You can revert socket.io by running the following
npm i socket.io#2.0.3
Or by changing the version number in package.json and then running
npm install
I have this directory structure:
app/router.js
app/oauth2-home-client/oauth2-client.js
And the sources:
app/oauth2-home-client/oauth2-client.js
//SOME CODE
exports.Bearer = {
authenticate : passport.authenticate('bearer', { session : false }),
initialize : passport.initialize()
// session : passport.session()
};
app/router.js
var oauth2 = require('./oauth2-home-client/oauth2-client');
console.log(JSON.stringify(oauth2.Bearer));
//SOME CODE
When I print oauth2.Bearer (and oauth2, too) content, I get {}.
What am I doing wrong?
Thanks.
Your code:
exports.Bearer = {
authenticate : passport.authenticate('bearer', { session : false }),
initialize : passport.initialize()
// session : passport.session()
};
Will result in:
exports.Bearer = {
authenticate :undefined,
initialize : undefined
};
because both passport.authenticate and passport.initialize return undefined.
And the keys having the value undefined are omitted by JSON.stringify.
[...]If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).[...]
Its value may point to the module instantiation.
Have you try this?
module.exports = {...};
The issue here is, that i don't know how to pass some scope.data to expressjs when using ngResource, so then it can be used with express route to insert something to DB.
ExpressJS REST
router.route('/Data')
.get(function(req,res){
var username = req.username;
var collection = db.collection('users');
collection.find({username:username}).toArray(function (err, doc){
res.send(doc[0].pets);
});
})
.post(function(req,res){
!!//I would like to use some data from angular here//!!
var name = req.body.name;
var surname = req.bodysurname;
collection.update({username: username}, {
$push: {
"details": {
name: name,
surname: surname
}
}
}, function (err, result) {
if (err) throw err;
});
});
Angular Factory
(function() {
'use strict';
angular
.module('App')
.factory('Factory', function ($resource) {
return $resource("/Data",{},
{ get:{ method:"GET",
cache:true,
isArray:true},
save:{ method:"POST",
cache:true,
isArray:false
}});
});
})();
Controller.js
This one works fine i use this function with ng-click()
$scope.load = function(){
Factory.get(function (data){
$scope.data = data;
});
};
With this one i have problem i have ng-models name and surname in view and i would like to send them to server so it can be used in REST route as req.body.name and req.body.surname.
$scope.AddData = function(){
Factory.save()
});
};
I think that data should be passed here in this function AddData, however i haven't succeeded yet.
So i tried as Jesus said but without results?
$scope.AddData = function(){
Factory.save($scope.name) //I tried ({name:$scope.name}) too
});
};
After advices from Jesús Quintana I checked the details of method POST and it occurred that everything was all right on angular side i was missing extension of body parser on server Express side
app.use(bodyParser.json())
So now it looks like this
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
NgResource works like a class with private and public methods:
For example Factory.save() is a public method and you must pass the data to be stored in the server, for example:
$scope.AddData = function(factoryData){
Factory.save(factoryData);
});
};
But also have a private method and the above example is the same to this:
$scope.AddData = function(factoryData){
var factory = new Factory(factoryData);
factory.$save(); // Is the same method but is private because factory is a instance of the factory
});
};
Both example are valid methods but must be used of differents ways.
EDIT
I create this little plunkr to see the network request: http://plnkr.co/edit/1bdblyrsW0jr7rXIAVNn?p=
I have a pdf file located here:
/server/.files/users/test.pdf
When I display a link on a page, I'd like for the user to be able to click the link, and for the pdf to be rendered on the screen.
I've read through this SO post in particular, and others, but cannot seem to get things to work: SO Link
I tried using an IR route server side, but every time I try even something simple I get the following error:
Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions. at Object.Meteor.userId
(packages/accounts-base/accounts_server.js:19:1) at Object.Meteor.user
(packages/accounts-base/accounts_server.js:24:1) at [object
Object].Router.onBeforeAction.except
(app/both/3-router/routes.js:10:15) at
packages/iron:router/lib/router.js:277:1 at [object
Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
at [object Object].hookWithOptions
(packages/iron:router/lib/router.js:276:1) at boundNext
(packages/iron:middleware-stack/lib/middleware_stack.js:251:1) at
runWithEnvironment (packages/meteor/dynamics_nodejs.js:108:1) at
packages/meteor/dynamics_nodejs.js:121:1 at [object Object].dispatch
(packages/iron:middleware-stack/lib/middleware_stack.js:275:1)
Line: #10 in my router.js file is the first if statement here:
Router.onBeforeAction(function () {
if (!Meteor.user() || Meteor.loggingIn()) {
this.redirect('welcome.view'); } else {
Meteor.call("userFileDirectory", function (error, result) {
if (error)
throw error;
else
console.log(result);
});
this.next();
} }, { except: ['welcome.view'] });
I tried this:
Router.map(function() {
this.route('serverFile', {
where: 'server',
path: /^\/uploads_url_prefix\/(.*)$/,
action: function() {
var filePath = process.env.PWD + '/.files/users/' + this.params[1];
var data = fs.readFileSync(filePath);
this.response.writeHead(200, {
'Content-Type': 'image'
});
this.response.write(data);
this.response.end();
}
}); });
But I'm not sure what to put in the path.
With process.env.PWD you are in the directory of your meteor project.
so you should be able to access your file like this:
var file = process.env.PWD + "/server/.files/users/test.pdf"
To use the fs package of node you also need to include it and you need to be on the server:
Router.route('/pdf', function() {
var filePath = process.env.PWD + "/server/.files/users/test.pdf";
var fs = Meteor.npmRequire('fs');
var data = fs.readFileSync(filePath);
this.response.write(data);
this.response.end();
}, {
where: 'server'
});
Make sure to this package to your project (https://atmospherejs.com/meteorhacks/npm)
meteor add meteorhacks:npm
I tested it and it is working like a charm!
I am trying to send some JSON data from express to Backbone model.
Update
console.log(response) in parse function of Model prints the values as {"version":"1.0","balance":"80.0"}
console.log(this.model) in the render() function of View gives {}
Server Side Node JS
var express = require('express');
var app = express();
app.listen(3000);
app.get('/getInfo', function(req, res){
//res.setHeader('Content-Type', 'application/json');
res.json({version: "1.0", balance: "80.0"});
});
On Node JS side I have tried following:
app.get('/getInfo', function(req, res){
res.setHeader('Content-Type', 'application/json');
res.send({version: "1.0", balance: "80.0"});
});
Backbone Model
var Bitcoin = Backbone.Model.extend({
url:'http://localhost:3000/getInfo',
parse: function(response) {
console.log(JSON.stringify(response));
return response;
}
});
var info = new Bitcoin ();
info.fetch();
It works fine if I change it to
var info = new Bitcoin ({version: "1.0", balance: "80.0"});
Backbone View
var BitcoinView = Backbone.View.extend({
id:'info',
class:'bitcoin',
template: _.template('<span> <%= balance %> </span>'+
'<span><%= version %></span>'),
render: function() {
console.log(JSON.stringify(this.model));
var attributes = this.model.toJSON();
this.$el.html(this.template(attributes));
}
});
var bitcoinView = new BitcoinView({model: info});
bitcoinView.render();
$('#app').html(bitcoinView.el);
Console
Uncaught ReferenceError: balance is not defined (from View)
XHR finished loading: "http://localhost:3000/getInfo".
{"version":"1.0","balance":"80.0"} (from parse function)
You are likely trying to render a model that has no values for certain attributes. The template of your view expects these attributes to be present. Add defaults to your model to prevent the template method from causing an error.
var Bitcoin = Backbone.Model.extend({
url:'http://localhost:3000/getInfo',
parse: function(response) {
console.log(JSON.stringify(response));
return response;
},
defaults: {
balance: "",
version:""
}
});
In your view you bind the render to the change event of your model. This way the view will rerender when the model changes (the data is fetched).
this.model.bind("change", this.render, this);
Alternativaly, you need to make sure the data is fetched before rendering the view. You can use the callback function of the fetch function.
info.fetch({
success: function(){
//create view here
}
});