How to pass model result to view in node js - node.js

I m new to node.js, try to making some small app.All is good but i m not able to pass model data outside the function:
Working Code is:
usermodel = require('../models/usermodel.js');
exports.index = function(req, res) {
var stuff_i_want = '';
usermodel.userlist(req, function(result)
{
//stuff_i_want = result;
res.render('index.ejs', {
title: "Welcome to Socka | View Players",
players:result
});
//rest of your code goes in here
});
console.log(stuff_i_want);
};
but i want some thing like:
usermodel = require('../models/usermodel.js');
exports.index = function(req, res) {
var stuff_i_want = '';
usermodel.userlist(req, function(result)
{
stuff_i_want = result;
//rest of your code goes in here
});
console.log(stuff_i_want);
res.render('index.ejs', {
title: "SA",
players:result
});
};

You are calling an asynchronous function userlist that's why you need it inside the callback. You can use Promise libraries like bluebirdjs and refactor your usermodel implementation to standard nodejs function arguments (err, result). In this case you can refactor it like the this below
const usermodel = require('../models/usermodel.js');
exports.index = async function(req, res) {
const players = await new Promise((resolve, reject) => {
usermodel.userlist(req, function(result){
resolve(result)
});
})
res.render('index.ejs', {
title: "SA",
players
});
};

Related

How to display an array like a hypertext in node.js

I want to display rooms array like hypertext but after the first room is created it gives an error.
let rooms = [];
app.get("/rooms", function(req, res) {
for(let i=0;i<rooms.length;i++) {
res.send(''+rooms[i]+' <br>');
}
res.send(200)
});
app.get("/room/:roomName", function(req, res) {
let roomName = req.params.roomName;
rooms.push(roomName);
res.send(roomName);
});
Actually the problem is with res.send since it shows that server have responded to client. So you can try following code
app.get("/rooms", function(req, res) {
let responseString = "";
for(let i=0;i<rooms.length;i++) {
responseString += (''+rooms[i]+' <br>');
}
res.status(200).send(responseString)
});
I think the code below fixes your problem,
// Rooms must be const, didn't modified, push is enumerating not overriding.
const rooms = []
// Prefer arrow functions for better looking code
// If you don't use req, just _
app.get('/rooms', (_, res) => {
// Map over rooms,
const response = rooms.map(room => `${room}`)
res.send(response)
})
app.get('/room/:name', (req, res) => {
const { name } = req.params
rooms.push(name)
res.send(name)
})
app.get('/rooms', (req, res) => {
try {
return res.status(200).json(rooms.map(room =>(`${room}`)));
} catch(error) {
console.error(error);
return res.status(500).json(error); // or another status you decide.
}
})

Assign value to variable outside mongo query in nodejs

Right now i have this code
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data)
postData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Events.list().then(data=> {
var jsonOutput=JSON.stringify(data)
eventData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data)
messageData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Users.list().then(data=> {
var jsonOutput=JSON.stringify(data)
userData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
//Then when all data from colections is retrieve i want to use the 4 variables that i created in the beggining
});
So basicly im trying to retrieve the data from my mongo database and then assign the results to that 4 variables that i create, but im not getting success.
For what i´ve been seeing i have to use async but im having some trouble doing it.
I don't like too much mrlanlee solution. This is a typical situation where using async / await can really make sense. Anyway, the Hugo's solution (the second one, with async await), even if it just works, will make the four queries in sequence, one after another to. If you want a clean, working and parallel solution, check this:
router.get('/export', async function(req, res, next) {
let data
try {
data = await Promise.all([
Posts.list(),
Events.list(),
Messages.list(),
Users.list()
]);
// at this point, data is an array. data[0] = Posts.list result, data[1] = Events.list result etc..
res.status(200).json(data)
} catch (e) {
res.status(500).send('error');
}
});
The other answer from Sashi is on the right track but you will probably run into errors. Since your catch statement on each promise returns 500, if multiple errors are caught during the query, Express will not send an error or 500 each time, instead it will throw an error trying to.
See below.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
try {
postData = Posts.list().then(data=> {
return JSON.stringify(data);
});
eventData = Events.list().then(data=> {
return JSON.stringify(data)
});
messageData = Messages.list().then(data=> {
return JSON.stringify(data);
})
userData = Users.list().then(data=> {
return JSON.stringify(data)
});
} catch (err) {
// this should catch your errors on all 4 promises above
return res.status(500).send('error')
}
// this part is optional, i wasn't sure if you were planning
// on returning all the data back in an object
const response = {
postData,
eventData,
messageData,
userData,
};
return res.status(200).send({ response })
});
For explanation of why you weren't able to mutate the variables, see Sashi's answer as he explains it.
The variables defined outside the async code is out of scope of the async functions. Hence you cannot store the returned value from the async functions in those variables.
This should work.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
postData = Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
eventData = Events.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
messageData = Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
userData = Users.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
});
Using Async/Await is a much neater solution.
router.get('/export', async function(req, res, next) {
var postData, eventData, messageData, userData;
try{
postData = await Posts.list();
eventData = await Events.list();
messageData = await Messages.list()
userData = await Users.list();
catch (e){
res.status(500).send('error');
}
});

Calling an API endpoint from within another route in Node / Express

I have myRoute.js with a route (GET) defined and I want to call an api endpoint from another route (api.js), and I'm not sure what the right way to do this is. The api.js route is working properly (image and code below).
api.js
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
works as expected:
myRoute.js
I would like when a user goes to localhost:3000/USER_ID that the route definition gets information from the api. Psuedo code below (someFunction).
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
let fromApi = someFunction(`localhost:3000/getAllGroups/${uid}`); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Not sure if i understand you correct but anyway i will try to help. So you have an api like
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
If you would like to reuse it you can extract a function from the code above like so:
async function getAllGroupsByUserId(uid){
const result = [];
try{
const data = await db.getAllGroups(uid);
for (i in data) {
result.push(data[i].groupname);
};
return result;
}
catch(e) {
return e;
}
}
And then reuse it in your api & anywhere you want:
router.get('/getGroups/:uid', async function(req, res, next) {
const uid = req.params.uid;
const groups = await getAllGroupsByUserId(uid);
res.status(200).send(groups);
})
Same you can do in your another route:
router.get('/:uid', async function(req, res, next) {
const uid = req.params.uid;
const fromApi = await getAllGroupsByUserId(uid); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Seems like pretty clear :)
I would use fetch for this. You can replace someFunction with fetch, and then put the res.render code in a .then(). So, you would get this:
const fetch = require("node-fetch");
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}').then(res => res.json()).then(function(data) {
returned = data.json();
console.log(returned); //expecting array
res.render('./personal/index.jade', {JSON.stringify(returned)});
});
});
A more robust way with error handling would be to write something like this:
const fetch = require("node-fetch");
function handleErrors(response) {
if(!response.ok) {
throw new Error("Request failed " + response.statusText);
}
return response;
}
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}')
.then(handleErrors)
.then(res => res.json())
.then(function(data) {
console.log(data) ; //expecting array
res.render('./personal/index.jade', {JSON.stringify(data)});
})
.catch(function(err) {
// handle the error here
})
});
The ideal way would be to abstract your code into a method so you aren't calling yourself, as The Reason said. However, if you really want to call yourself, this will work.

How to make reusable function code in Expressjs?

I am newbie in ExpressJs and module pattern in my project. Now, i am stuck that how to use created controller function in another controller. Please look at example :-
menu.ctrl.js
------------
module.exports.save=function(req,res,next){
//here some logic
//somethings like validate req.body,etc
menu.save(function(err){
if(err) return next(err);
res.json({msg:'menu save'})
})
}
user.ctrl.js
------------
var user=require('./user.model')
var menuCtrl=require('./menu.ctrl')
module.exports.save=function(req,res,next){
//here some logic
user.save(function(err){
if(err) return next(err);
//HERE I WANT TO USE `menuCtrl.save()` function
res.json({msg:'success'});
})
}
Decoupling your controller logic from your model logic will allow you reuse logic and make your application easier to maintain.
The idea is that controllers' purpose is to format input and output to and from you application, while models handle actual data manipulation. (This is a typical Rails-like MVC pattern for REST APIs)
To your example:
menuController.js
var menuModel = require('./menuModel');
module.exports.save = function(req, res, next) {
menuModel.save(req.body, function(err) {
if(err) return next(err);
res.json({msg:'menu save'})
});
};
menuModel.js
module.exports.save = function(body, callback) {
// Save menu to the DB
menu.save(body, callback);
};
userController.js
var userModel = require('./userModel');
module.exports.save = function(req, res, next) {
userModel .save(function(err){
if(err) return next(err);
res.json({msg:'success'});
});
}
userModel.js
var menuModel = require('./menuModel');
module.exports.save = function(body, callback) {
// Save user to the DB
user.save(body, function(err, res) {
if (err) return callback(err);
menuModel.save(body, callback);
});
};
Rule of thumb, keep as less business logic as possible in controllers.
//Here is a solution if you are using same route file
//
var getNotificationSetting = async function (user_id) {
let params = {}
params = await NotifcationSetting.findAll({
where: { ns_user_id : user_id },
});
return params;
}
//now calling in action
router.get('/', async function(req, res, next) {
let params = {}
//for setting section
params = await getNotificationSetting(req.session.user.user_id);
});

Express JS page rendered before async function was executed

I have a problem with express.js routing. I want to get all restaurants when users open /restaurant/map. Problem is that function gm.geocode is async so page is rendered before that function is executed. How would I get variabile mapsData at the right time, so when page /restaurant/map is loaded? What is the right way to do that?
var express = require('express');
var router = express.Router();
var gm = require('googlemaps');
var fs=require('fs');
var staticDB=require('./staticDB.js');
var mapsData=[];
/* GET home page. */
router.get('/', function (req, res, next) {
//not important
});
router.get('/map/', function (req, res, next) {
var counter=0;
console.log('Request URL:', req.originalUrl);
for (i = 0; i < staticDB.addresses.addresses.length; i++) {
gm.geocode(staticDB.addresses.addresses[i].street + ", " + staticDB.addresses.addresses[i].city, function (err, data){
mapsData.push({
"name": staticDB.restaurants.restaurants[counter].name,
"location": data.results[0].geometry.location
});
counter++;
}, false);
}
res.render('map', {
title: 'title',
restaurants: mapsData
});
});
module.exports = router;
Never use regular for loops to deal with asynchronous flows. There are modules that handle those scenarios for you, like async, or even promises. In your case, your code should look like this, using async.each (I didn't test it):
router.get('/map/', function (req, res, next) {
var counter=0;
console.log('Request URL:', req.originalUrl);
async.each(staticDB.addresses.addresses, function (address, cb) {
gm.geocode(address.street + ", " + address.city, function (err, data){
mapsData.push({
"name": staticDB.restaurants.restaurants[counter].name,
"location": data.results[0].geometry.location
});
counter++;
cb();
}, false);
}, function (err) {
if (err) {
return res.render('error', { error: err });
}
res.render('map', {
title: 'title',
restaurants: mapsData
});
});
});

Resources