I'm a bit new in Node.JS and Express framework and I have a great problem with the code below:
app.get('/student', function(req, res) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if(rows.length !== 0) {
/* Save data. */
}
else
res.render('incorrect_student'); /* Render the error page. */
});
dbRequest = 'SELECT * FROM Groups WHERE Name = \'' + req.query['group'] + '\'';
db.all(dbRequest, function(error, rows) {
/* Add selected data to previous saved data. */
}
});
res.render('student', {data: /* data from both queries above */});
});
As I have written in comment blocks, I would like to: execute first select query, save data from rows object, execute second query, again save received data in other object and then finally render the page passing data from both queries. My question is, what is the best way to do that?
I know that there is a problem caused by anonymous function. I have tried to fix the problem for over five hours as follows:
Clone rows object to another in anonymous function and then pass it to res.render. This solution dosen't work, because values of copied object are not visible (undefined) outside this function - only inside it.
Render the student page twice - it was really naive of course.
Change db.all command to db.prepare and then db.run - it wasn't working too.
Return object by the anonymous function and then assign it to external object defined between app.get and var dbRequest. The result was as described in 1st point.
I have also an idea to create "subpages" containig parts of student page, which need variables from only one query. The other idea is to use some other functions of db, req, res or app objects. But, as I said before, I'm new in Express and I don't know how to realize my above ideas.
Please note that it is impossible to join tables - in fact, I want to make 4-5 queries and then render my view. I'm using SQLite3 database.
Thank you very much for your help! I hope that you'll help me to solve my problem.
In your situation, I would split up the database calls into separate calls, and make use of the next middleware function.
It would looks something like:
function findStudent(req, res, next) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if(rows.length !== 0) {
req.students = rows;
return next();
}
res.render('incorrect_student'); /* Render the error page. */
});
}
function findGroups(req, res, next) {
dbRequest = 'SELECT * FROM Groups WHERE Name = \'' + req.query['group'] + '\'';
db.all(dbRequest, function(error, rows) {
/* Add selected data to previous saved data. */
req.groups = rows;
next();
}
});
}
function renderStudentsPage(req, res) {
res.render('student', {
students: req.students,
groups: req.groups
});
}
app.get('/student', findStudent, findGroups, renderStudentsPage);
When you GET /student, you first call findStudent. Once the db call is finished, it will either render an error page, or call next(). Calling next goes to the next function, findGroups, which will then call renderStudentsPage. You can store the data on the req object as you move down the line of functions.
Hope this helps, and here is more info:
http://expressjs.com/guide/using-middleware.html
edit/note:
I did not mention it earlier, but if you pass in an argument when calling next(), you will trigger the error handling state. Convention dictates next() is left parameter-less unless you have met an error instance.
You want to separate out the UI rendering aspect from the database call, so going further, your code could look like:
function findStudent(req, res, next) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if (error || !rows.length) {
return next(error);
}
req.students = rows;
return next();
});
}
And then elsewhere in your code you can handle rendering error pages.
I know this is an old question, but for anybody still having problems and using MongoDB instead this is what worked for me.
//index.js
const express = require('express');
const router = express.Router();
function getData (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.find({}, {}, function(e, docs) {
req.data = docs;
return next();
});
}
function getVendor (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.distinct("vendor", function(e, docs) {
req.vendor = docs
next();
});
}
function getType (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.distinct("type", function(e, docs) {
req.type = docs
next();
});
}
function renderData(req, res) {
res.render('index', {
data: req.data,
vendor: req.vendor,
type: req.type
});
}
/* GET home page. */
router.get('/', getData, getVendor, getType, renderData);
module.exports = router;
Then inside your ejs file
//index.ejs
<body>
<div id="app">
<h1>Choose a Vendor</h1>
<template v-for="vendor in values.vendor">
<label :for="vendor">{{ vendor }}</label>
<input type="radio" :value="vendor" v-model="flagpole.vendor">
</template>
<div>
<template v-for="type in values.type">
<label :for="type">{{ type }}</label>
<input type="radio" :value="type" v-model="flagpole.type">
</template>
</div>
</div>
<script type="text/javascript">
var vendor = <%- JSON.stringify(vendor) %>
var type = <%- JSON.stringify(type) %>
var vm = new Vue({
el: '#app',
data: {
values: {
vendor: vendor,
type: type
},
flagpole: {
vendor: '',
type: ''
}
},
Related
I want to display User Verification menu only if user is not verified in node js
header.ejs
<% if(!isVerified(user.id)){%>
<li>User Verification</li>
<% } %>
function.js
module.exports.isVerified = function(user_id) {
return new Promise(function(resolve, reject) {
var sql = 'SELECT * FROM `users` WHERE id='+mysql.escape(user_id)+' and verified=1';
config.db.query(sql,function (err, rows){
if(err){
reject(false);
}else{
resolve(rows);
}
});
});
}
but it is not showing the menu....what should i do? I don't know i'm new in nodejs. Thanks in advance
Finally after a week i have solved this problem
function.js (same as i have posted in question)
module.exports.isVerified = function(user_id) {
return new Promise(function(resolve, reject) {
var sql = 'SELECT * FROM `users` WHERE id='+mysql.escape(user_id)+' and verified=1';
config.db.query(sql,function (err, rows){
if(err){
reject(false);
}else{
resolve(rows);
}
});
});
}
now the change that i have done here is in app.js and header.ejs file.
app.js
const fn = require('./helper/function_helper')
app.use(async function(req, res, next) {
if(req.session.user.id){
res.locals.user = await fn.isVerified (req.session.user.id);
//so now i can access user.verified in header.ejs file.
}
next();
});
header.ejs
//instead of using the isVerified() function I have used the user.verified
<%if(user.verified==0){%>
<li>User Verification</li>
<%}%>
Your promise doesn't return a boolean, it wait and indicate if the SQL query worked or not, you need to return a boolean in both case.
Since you are using a promise, you are dealing with asynchronous programming. What that means is that, your returns are unpredictable. How you should fix that is defining promise separately and check it with a code block similar to .then(function(res){...})
I recommend you to look at these resources:
Javascript Promises
How to return inside promise?
use a module for node that provide synchronous functions. Here you'll find a module that provide sync/async functions to deal with mysql:
node-mysql-libmysqlclient
//Sample code
module.exports.isVerified = function (user_id) {
var config = require("./config.json") ;
var mysql = require('mysql-libmysqlclient') ;
var client = mysql.createConnectionSync(config.host, config.user, config.password, config.database) ;
var flag = false;
var query = 'SELECT * FROM `users` WHERE id=' + mysql.escape(user_id) + ' and verified=1' ;
var handle = client.querySync(query) ;
var results = handle.fetchAllSync() ;
if (results.length > 0)
{
flag = true;
} else
{
flag = false;
}
console.log(JSON.stringify(results)) ;
return flag;
}
I don't know if this fix your problem, but your ejs code is wrong. You have not closed the ejs tags. This is the proper syntax
<% if(!isVerified(user.id)){ %>
<li>User Verification</li>
<% } %>
I'm using node and postgres, I'm new to writing async function, what I'm trying to do is a very simple query that will do a total count of records in the database, add one to it and return the result. The result will be visible before the DOM is generated. I don't know how to do this, since async function doesn't return value to callers (also probably I still have the synchronous mindset). Here's the function:
function generateRTA(callback){
var current_year = new Date().getFullYear();
const qry = `SELECT COUNT(date_part('year', updated_on))
FROM recruitment_process
WHERE date_part('year', updated_on) = $1;`
const value = [current_year]
pool.query(qry, value, (err, res) => {
if (err) {
console.log(err.stack)
} else {
var count = parseInt(res.rows[0].count) + 1
var rta_no = String(current_year) + '-' + count
callback(null, rta_no)
}
})
}
For the front-end I'm using pug with simple HTML form.
const rta_no = generateRTA(function (err, res){
if(err){
console.log(err)
}
else{
console.log(res)
}
})
app.get('/new_application', function(req, res){
res.render('new_application', {rta_number: rta_no})
});
I can see the rta_no in console.log but how do I pass it back to the DOM when the value is ready?
Based on the ajax call async response, it will update the div id "div1" when it gets the response from the Node js .
app.js
app.get("/webform", (req, res) => {
res.render("webform", {
title: "Render Web Form"
});
});
app.get("/new_application", (req, res) => {
// Connect to database.
var connection = getMySQLConnection();
connection.connect();
// Do the query to get data.
connection.query('SELECT count(1) as cnt FROM test ', function(err, rows, fields) {
var person;
if (err) {
res.status(500).json({"status_code": 500,"status_message": "internal server error"});
} else {
// Check if the result is found or not
if(rows.length==1) {
res.status(200).json({"count": rows[0].cnt});
} else {
// render not found page
res.status(404).json({"status_code":404, "status_message": "Not found"});
}
}
});
// Close connection
connection.end();
});
webform.pug - Via asynchronous call
html
head
script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')
script.
$(document).ready(function(){
$.ajax({url: "/new_application", success: function(result){
$("#div1").html(result.count);
}});
});
body
div
Total count goes here :
#div1
value loading ...
That seems okay, I'm just not sure of this:
The result will be visible before the DOM is generated
This constraint defeats the purpose of async, as your DOM should wait for the returned value to be there. Instead of waiting for it you could just render the page and once the function returns and runs your callback update the value.
Also, perhaps it's worth having a look into promises
I'm trying to use node js and mysql, along with rendering data from my database into a view.
The issue is, how do I render multiple pieces of data within 1 view (Using jade template).
router.get('/about', function(req, res) {
var animalsData = sequelize.query("SELECT * FROM animals").success(function(rows) {
return rows;
});
console.log(animals); // returns 'undefined'
var humansData = sequelize.query("SELECT * FROM humans").success(function(rows) {
return rows;
});
console.log(humans); // returns 'undefined'
res.render('about', { animals: animalsData, humans: humansData });
});
As you can see, how can I get the data and then pass it into the view? Using nested callbacks can get messy if theres 5 or 6 bits of data (mysql queries) I wish to pass into the view?
Thanks
First of, those returns don't work. The code that you are running is asynch. I would suggest you to use promises:
var q = require("q");
router.get('/about', function(req, res) {
var animalDeferer = q.defer(),
humanDeferer = q.defer;
sequelize.query("SELECT * FROM animals").success(function(rows) {
animalDeferer.resolve(rows);
});
sequelize.query("SELECT * FROM humans").success(function(rows) {
humanDeferer.resolve(rows);
});
Q.all([animalDeferer.promsie, humanDeferer.promise]).spread( function(animalsData, humansData) {
res.render('about', { animals: animalsData, humans: humansData });
});
});
I wrote this directly on the answerbox so it may have typos.
q - https://github.com/kriskowal/q
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
});
Error
Problem: Cannot read property 'length' of undefined at
jade_debug.unshift.lineno (eval at
(C:\Users\Dev\Node_js\node_modules\jade\lib\jade.js:160:8),
:111:31) at eval (eval at
(C:\Users\Dev\Node_js\node_modules\jade\lib\jade.js:160:8),
DB function
exports.selectRows = function(){
var objBD = BD();
objBD.query('SELECT * FROM usr ', function(results) {
return(results);
});
}
Route
exports.index = function(req, res) {
res.render('customer/index',{ customers: db.selectRows() });
};
index.jade
each item in customers
tr
td
a(href='/customer/details/#{item.id}') #{item.id}
td #{item.name}
td #{item.email}
td #{item.phone}
Problem with your code is that the selectRows method is executed asynchronously and db.selectRows() expression in your handler method always return undefined value and hence the execption (customers template variable is undefined).
You should add the following changes to your code in order to have it working correctly:
DB function :
exports.selectRows = function(callback){
var objBD = BD();
objBD.query('SELECT * FROM usr ', callback);
}
Route:
exports.index = function(req, res) {
db.selectRows(function(results){
res.render('customer/index', { customers: results });
});
}
Sometimes you may have a situation (very common Node.js pattern) where your callback gets two parameters:
first would be an error - it should be undefined if DB query was successful
second would be DB query results data
In case of two parameters (error and results) your route should look as follows:
exports.index = function(req, res) {
db.selectRows(function(err, results){
if (err) return res.send(500, "DB QUERY ERROR");
res.render('customer/index', { customers: results });
});
}
You can also simplify your index.jade
each item in customers
tr
td: a(href='/customer/details/#{item.id}')= item.id
td= item.name
td= item.email
td= item.phone
I hope that will help.