How do I call the function expression "extractUserProgress" which is situated in an external module from server.js?
EDIT
I have clarified further what is happening in my code.
I have a chain of function expressions in my module that follow from "extractUserProgress". The last function returns an array which is what I'm after.
//setGen.js (module file)
module.exports = function(app, db) {
var extractUserProgress = function() {
//Access mongoDB and do stuff
nextFunction(x)
}
var nextFunction = function(x) {
let y = [];
//calculate y
return y // this is what i'm after
}
}
//server.js
const setGen = require("./setGen")
app.get("/setGen", function(req, res){
//data here from select input
extractUserProgress //How to call from here?
console.log(y) //array from module
});
I have required the module in server.js but not sure how to export function in this scenario where the functions in module also needs to access mongoDB.
Thanks
You can achieve this easily if you change the structure of your exports a little.
const extractUserProgress = function (app, db) {
console.log('This can be called');
//Access mongoDB and do stuff
}
module.exports = {
extractUserProgress
};
you can call this function from the otherside this way.
const newFile = require('./server');
newFile.extractUserProgress(); // you can pass arguments APP and DB to this function
With the code as-is, you can't - extractUserProgress is not accessible, it's declared inside the exported function scope.
If you need it accessible, and also need to keep the exported signature, then you can return a hash of functions e.g.
module.exports = function(app, db) {
...
return {
extractUserProgress(...) {
...
},
// More functions
}
}
// Usage
const setGen = require('./setGen')(app, db)
setGen.extractUserProgress(...);
If you don't need to maintain the existing exported function, then you can export the functions as a hash instead
module.exports = {
extractUserProgress(...) {
...
},
// More functions
}
// Usage
const setGen = require('./setGen')
setGen.extractUserProgress(...);
I am trying to facilitate and organize my work between socket communication and Node.JS (My project is too big)
Instead of socket.emit() function in client-side I use a function that I created:
function socketEmit(socketName, functionName, data){
socket.emit(socketName, {functionName: functionName, data: data});
}
socketEmit('exampleClass', 'exampleFunction', 'test');
socketEmit('exampleClass2', 'exampleFunction2', 'test');
After emit, I get the socket (socket.on) on the server-side with this function. And register it in the class depending on the "socketName"
socket.on('exampleClass', function (socketData){
var exampleClass = require('./server/exampleClass.js');
exampleClass.registerSocket(socket, socketData);
})
socket.on('exampleClass2', function (socketData){
var exampleClass2 = require('./server/exampleClass2.js');
exampleClass2.registerSocket(socket, socketData);
})
Within the classes has this function "registerSocket" to perform the same
exampleClass.js
module.exports = {
registerSocket: function(socket, socketData){
var functionName = socketData.functionName;
var data = socketData.data;
[functionName](socket, data);
},
}
function exampleFunction(socket, data){
console.log('test');
}
However, I have an error in this part in registerSocket function:
[functionName] is not a function
You're trying to call a an array [functionName](socket, data);:
const array = [functionName]; // array
array(socket, data); // you can't :)
Put the functions in an object, and access it using [] notation.
const fns = {
exampleFunction(socket, data){
console.log('test');
}
};
module.exports = {
registerSocket: function(socket, socketData){
var functionName = socketData.functionName;
var data = socketData.data;
fns[functionName](socket, data);
},
}
I was trying to call a function in the constructor from prototype but keep getting the below error and I dont know whats wrong with my code.
TypeError: this.authorize is not a function
This is my code:
controller.js
var Controller = function() {
this.authorize = function(req, res) {
if (!req.user) {
res.redirect("/");
}
};
};
Controller.prototype.online = function(req, res) {
this.authorize(req, res);
res.render('./play/online');
};
var controller = new Controller();
module.exports = controller;
route.js
var router = require('express').Router();
var controller = require('../controller');
router.get('/online', controller.online);
module.exports = router;
If I put authorize function outside of Controller then I can call it but I don't want do that.
So what can I do?
Update:
This error occurs in Nodejs when I apply the request "/online", not in pure Javascript
You are loosing context when passing online as a callback
router.get('/online', controller.online.bind(controller));
Or inside constructor
var Controller = function() {
this.authorize = function(req) {
console.log(req);
};
this.online = this.online.bind(this);
};
Set the authorize function on the prototype of Controller like you did with the online function.
Edit: I tested your code (not using Controller.prototype) and it works for me...
I can call authorize within the online function. Does the error occur when you call authorize from the online function or does it occur somewhere else? Are you sure there isn't a typo in your code?
Could you try to define your online function in the constructor as well?
//Your initial version: works for me...
var Controller = function() {
this.authorize = function(req) {
console.log(req);
};
};
Controller.prototype.online = function(text) {
this.authorize(text);
};
var controller = new Controller();
controller.online("Some text");
//My prototype version: works as well...
var Controller2 = function() {};
Controller2.prototype.authorize = function(req) {
console.log(req);
};
Controller2.prototype.online = function(text) {
this.authorize(text);
};
var controller2 = new Controller2();
controller2.online("Some text2");
I'd like to have a node module, which exports a factory that returns instances of a class function.
//myModule.js
function MyClass(options) {
this.options = options || {};
}
MyClass.prototype.handle = function(req, res, next) {
//I want to access the instance here in the middleware function
console.log(this.options);
}
module.exports = function(options) {
return new MyClass(options);
}
Then, inside server.js, I'm attaching this middleware:
var myInstance = require("./myModule")({
foo: "bar"
});
app.use(myInstance.handle);
Inside the middleware function, this refers to something else (the global object maybe?), but I want to access the instance containing options. The only solution I could come up with was calling myInstance.handle.bind(myInstance), but that's not too friendly to the module's users. Is there another way to solve this problem, or is there a better way to do this kind of pattern entirely?
It's better to avoid using prototype whenever you can, and you can achieve what you're doing using something like this:
module.exports = function(opts) {
// private methods/vars
var options = opts || {};
return {
handle: function(req, res, next) {
console.log(opts);
}
}
}
Every time I update the database with a new menu item, I'm trying to get the routing to update with one more route. Here's my sad little ugly attempt:
Here in app.js, I check the menu database and shazaam...routes are made on the fly at startup. Cool!:
// in app.js //
var attachDB = function(req, res, next) {
req.contentdb = db.content;
req.menudb = db.menu;
req.app = app; // this is the express() app itself
req.page = PageController;
next();
};
db.menu.find({}, function (err, menuitems){
for(var i=0; record = menuitems[i]; i++) {
var menuitem = record.menuitem;
app.all('/' + menuitem, attachDB, function(req, res, next) {
console.log('req from app all route: ',req)
PageController.run(menuitem, req, res, next);
});
}
http.createServer(app).listen(config.port, function() {
console.log(
'\nExpress server listening on port ' + config.port
);
});
});
Not real elegant but it's a proof of concept. Now here's the problem: When I save a new menu item in my Admin.js file, the database get's updated, the router seems to get updated but something about the request just blows up after clicking on a menu link with a dynamically created route
Many things in the request seem to be missing and I feel like there is something fundamental I don't understand about routing, callbacks or perhaps this is just the wrong solution. Here's what the function responsible for creating a new menu item and creating a new route in my Admin.js file looks like:
// in Admin.js //
menuItem: function(req, res, callback) {
var returnMenuForm = function() {
res.render('admin-menuitem', {}, function(err, html) {
callback(html);
});
};
var reqMenudb = req.menudb,
reqContentdb = req.contentdb,
reqApp = req.app,
reqPage = req.page;
if(req.body && req.body.menuitemsubmitted && req.body.menuitemsubmitted === 'yes') {
var data = { menuitem: req.body.menuitem };
menuModel.insert( data, function(err) {
if (err) {
console.log('Whoa there...',err.message);
returnMenuForm();
} else {
// data is inserted....great. PROBLEM...the routes have not been updated!!! Attempt that mimics what I do in app.js here...
reqApp.all('/' + data.menuitem, function(req, res, next) {
// the 2 db references below are set with the right values here
req.contentdb = reqContentdb;
req.menudb = reqMenudb;
next();
}, function(req, res, next) {
reqPage.run(data.menuitem, req, res, next);
});
returnMenuForm();
}
});
} else {
returnMenuForm();
}
},
Saving the data in the admin section works fine. If you console log app.routes, it even shows a new route which is pretty cool. However after refreshing the page and clicking the link where the new route should be working, I get an undefined error.
The admin passes data to my Page controller:
// in PageController.js //
module.exports = BaseController.extend({
name: "Page",
content: null,
run: function(type, req, res, next) {
model.setDB(req.contentdb); /* <-- problem here, req.contentdb is undefined which causes me problems when talking to the Page model */
var self = this;
this.getContent(type, function() {
var v = new View(res, 'inner');
self.navMenu(req, res, function(navMenuMarkup){
self.content.menunav = navMenuMarkup;
v.render(self.content);
});
});
},
getContent: function(type, callback) {
var self = this;
this.content = {}
model.getlist(function(records) {
if(records.length > 0) {
self.content = records[0];
}
callback();
}, { type: type });
}
Lastly, the point of error is here in the model
// in Model.js //
module.exports = function() {
return {
setDB: function(db) {
this.db = db;
},
getlist: function(callback, query) {
this.db.find(query || {}, function (err, doc) { callback(doc) });
},
And here at last, the 'this' in the getlist method above is undefined and causes the page to bomb out.
If I restart the server, everything works again due to my dynamic loader in app.js. But isn't there some way to reload the routes after a database is updated?? My technique here does not work and it's ugly to be passing the main app over to a controller as I'm doing here.
I would suggest two changes:
Move this menu attachment thing to a separate module.
While you're at it, do some caching.
Proof of concept menu db function, made async with setTimeout, you'll replace it with actuall db calls.
// menuitems is cached here in this module. You can make an initial load from db instead.
var menuitems = [];
// getting them is simple, always just get the current array. We'll use that.
var getMenuItems = function() {
return menuitems;
}
// this executes when we have already inserted - calls the callback
var addMenuItemHandler = function(newItem, callback) {
// validate that it's not empty or that it does not match any of the existing ones
menuitems.push(newItem);
// remember, push item to local array only after it's added to db without errors
callback();
}
// this one accepts a request to add a new menuitem
var addMenuItem = function(req, res) {
var newItem = req.query.newitem;
// it will do db insert, or setTimeout in my case
setTimeout(function(newItem){
// we also close our request in a callback
addMenuItemHandler(newItem, function(){
res.end('Added.');
});
}, 2000);
};
module.exports = {
addMenuItem: addMenuItem,
getMenuItems: getMenuItems
}
So now you have a module menuhandler.js. Let's construct it and use it in our app.
var menuHandler = require('./menuhandler');
var app = express();
// config, insert middleware etc here
// first, capture your static routes - the ones before the dynamic ones.
app.get('/addmenuitem', menuHandler.addMenuItem);
app.get('/someotherstaticroute', function(req, res) {
var menu = menuHandler.getMenuItems();
res.render('someview', {menu: menu});
});
// now capture everything in your menus.
app.get('/:routename', function(req, res){
// get current items and check if requested route is in there.
var menuitems = menuHandler.getMenuItems();
if(menuitems.indexOf(req.params.routename) !== -1) {
res.render('myview', {menu: menuitems});
} else {
// if we missed the route, render some default page or whatever.
}
});
app.get('/', function(req, res) {
// ...
});
Now you don't go to db if there were no new updates (since menuitems array is always up to date) so your initial view is rendered faster (for that 1 db call, anyway).
Edit: oh, I just now saw your Model.js. The problem there is that this refers to the object you have returned:
{
setDB: function(db) {
this.db = db;
},
getlist: function(callback, query) {
this.db.find(query || {}, function (err, doc) { callback(doc) });
}
}
So, no db by default. And since you attach something to the app in the initial pageload, you do get something.
But in your current update function, you attach stuff to the new app (reqApp = req.app), so now you're not talking to the original app, but another instance of it. And I think that your subsequent requests (after the update) get the scope all mixed up so lose the touch with the actual latest data.
In your code when you start your server it reads from the menu db and creates your routes. When your menu changes, you do not re-read from db again.
I suggest you do something like the following
app.all('*', function(req, res) {
//read from your menu db and do the the route management yourself
});