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.
Related
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 appologise for the horrendous title for this submission but I have been tearing my hair out for at least 8 hours now trying to solve this.
I initially took some guidance from sova's result on Express displaying mongodb documents in Jade Express displaying mongodb documents in Jade
However whenever I try and do the query it fails.
my index.js code is this.
app.get('/test', function (req, res) {
var MongoClient = require('mongodb').MongoClient
var url = 'mongodb://localhost/quizmaster';
var results_from_mongo = [];
MongoClient.connect(url, function (err, db) {
var str = db.collection('qmquestions').find();
str.each(function (err, doc) {
//console.log(doc);
results_from_mongo.push(doc);
console.log(results_from_mongo) //Push result onto results_array
});
//now we have a results array filled like this:
// results_from_mongo = ["some string", "some string", "some string"]
//so let's pass them to the jade file to render them.
res.render('test', {results_from_mongo : results_from_mongo });
});
});
My test.jade code is this
block content
h1= title
h2= "results from mongo:"
select
each results_from_mongos, i in results_from_mongo
option(value=i) #{results_from_mongos}
I have even tried the pug variation of this (test.jade)
table
thead
tr
th Question name
th Question
th Answer
tbody
each results_from_mongo, i in results
tr
td= results_from_mongo.questionTitle
td= results_from_mongo.question
td= results_from_mongo.answer
The db.collections.find direct from MongoDB result is
{ "_id" : ObjectId("58af574c4fef02081c32da2f"), "question" : { "questionTitle" : "Test", "question" : "Test", "answer" : "Test" } }
I have just tried so many different ways of trying to get it but all it equals is an empty result no matter what I do if anybody could help me out on this I would be greatly appreciated as I am just ripping my hair out on this and I feel it must be something so simple which I am missing. If anymore code is needed then I will edit the post with the code asked for.
Your index.js seems to be incomplete, but the biggest issue was that you didn't have a callback for the .find() call for mongo. Looking at the docs - https://www.npmjs.com/package/mongodb - it seems as though they use .toArray() which takes a callback. You need to place your res.render inside of that callback so that it gets executed after mongo returns results.
As of now you are executing res.render() when mongo hasn't gotten any data yet, due to the asynchronous nature of node.
index.js
const express = require('express')
const app = express()
var MongoClient = require('mongodb').MongoClient
app.set('view engine', 'pug')
app.get('/test', function (req, res) {
var url = 'mongodb://localhost/quizmaster';
//var url = 'mongodb://localhost:27017/quizmaster';
var results_from_mongo = [];
MongoClient.connect(url, function (err, db) {
//var str = db.collection('qmquestions').find();
var str = db.collection('qmquestions').find({}).toArray(function (err, docs){
if(err){
return res.send('error')
}
console.log(docs)
//return res.render('test', {results_from_mongo : results_from_mongo });
return res.render('test', {results_from_mongo : docs });
})// callback
//str.each(function (err, doc) {
// //console.log(doc);
// results_from_mongo.push(doc);
// console.log(results_from_mongo) //Push result onto results_array
//});
//now we have a results array filled like this:
// results_from_mongo = ["some string", "some string", "some string"]
//so let's pass them to the jade file to render them.
});
});
app.listen('3030', function(){
console.log("listening on 3030")
})
views/test.pug
block content
h1= title
h2= "results from mongo:"
select
// each results_from_mongos, i in results_from_mongo
each results_from_mongos, i in results_from_mongo
option(value=i) #{results_from_mongos}
ul
each item in results_from_mongo
li= item.question.questionTitle
Use the method toArray After your find method call. As This:
collection(...).find().toArray(function(err,doc)=>
//Do what you want
)
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: ''
}
},
SELECT always displays me undefined in the input text
Route:
exports.edit = function(req, res){
var id =(req.params.id);
customer = db.getCustomerById(id,function(results){
res.render('customer/edit', {customer: results });
});
};
DB function:
exports.getCustomerById = function(id,callback){
var objBD = BD();
objBD.query('SELECT * FROM user WHERE id=? ', id, callback);
};
Edit.jade :
form(id='form', method='POST', action='/customer/edit/#{customer.id}')
input(type='text', id='name', name='name' value='#{customer.name}')
input(type='email', id='email', name='email' value='#{customer.email}')
input(type='tel', id='phone', name='telephone' value='#{customer.phone}')
The callback you pass in db.getCustomerById should have the format callback(err, results). And I guess that results argument will be an array and so to pick the customer you will need to do customer = results[0].
Try the following code:
exports.edit = function(req, res){
var id =(req.params.id);
customer = db.getCustomerById(id,function(err, results){
if (err) {
console.log("Ops! Error trying to get customer ....");
throw err;
}
res.render('customer/edit', {customer: results[0] });
});
};
Hi im developing nodejs express app. I am getting exception and dont know why. Everything seem to perfect for me. My exception is shown below:
500 TypeError: C:\Nodejs\NodejsBlog\apps\blog/views/postdetail.jade:23<br/> 21| .col- md-12 <br/> 22| .posts <br/> > 23| h3= post.title <br/> 24| p=post.body <br/> 25| p tag: <br/> 26| i=post.tag <br/><br/>Cannot read property 'title' of undefined
21| .col-md-12
22| .posts
> 23| h3= post.title
24| p=post.body
25| p tag:
26| i=post.tag
Cannot read property 'title' of undefined
at eval (eval at (C:\Nodejs\NodejsBlog\node_modules\jade\lib\jade.js:152:8), :221:59)
at C:\Nodejs\NodejsBlog\node_modules\jade\lib\jade.js:153:35
at Object.exports.render (C:\Nodejs\NodejsBlog\node_modules\jade\lib\jade.js:197:10)
at Object.exports.renderFile (C:\Nodejs\NodejsBlog\node_modules\jade\lib\jade.js:233:18)
at View.exports.renderFile [as engine] (C:\Nodejs\NodejsBlog\node_modules\jade\lib\jade.js:218:21)
at View.render (C:\Nodejs\NodejsBlog\node_modules\express\lib\view.js:76:8)
at Function.app.render (C:\Nodejs\NodejsBlog\node_modules\express\lib\application.js:504:10)
at ServerResponse.res.render (C:\Nodejs\NodejsBlog\node_modules\express\lib\response.js:798:7)
at C:\Nodejs\NodejsBlog\apps\blog\routes.js:64:14
at callbacks (C:\Nodejs\NodejsBlog\node_modules\express\lib\router\index.js:164:37)
And here is the app.post code:
app.get('/Post/:id',function(req,res){
var postdata;
var comments;
Post.findOne({_id:req.params.id},function(err, docs){
if(docs) {
postdata=docs;
console.log('Gönderi bulundu');
console.log(docs);
console.log(postdata);
console.log(postdata.title);
} else {
console.log('Gönderi bulunamadı');
}
});
Comment.findOne({postid:req.params.id},function(err, docs){
if(docs) {
console.log('Yorum bulundu');
console.log(docs);
} else {
comments=docs;
console.log('Yorum bulunamadı');
}
});
return res.render(__dirname+"/views/postdetail",{
title: 'adfasdf',
stylesheet: 'postdetail',
post:postdata,
comments:comments
});
});
And my view:
extends ../../../views/bloglayout
block js
script(type='text/javascript')
$(function() {
$("#commentform" ).submit(function( event ) {
alert( "Handler for .submit() called." );
$.ajax({
url: '/Post/Comment/',
type: "POST",
data: $('#commentform').serialize(),
success: function(response){
alert('Yorum Kaydedildi');
}
});
event.preventDefault();
});
});
block content
.row
.col-md-12
.posts
h3=post.title
p=post.body
p tag:
i=post.tag
p Anahtar Kelimeler:
b=post.keywords
.row
.col-md-4
h5 Yorum Yap
form#commentform(role='form',action='/Post/Comment', method='post')
input(type='hidden',name='comment[postid]',value=postdata._id)
.form-group
input.form-control(type='email',name='comment[email]',placeholder='E-posta adresi')
.form-group
input.form-control(type='text',name='comment[website]', placeholder='Website')
.form-group
textarea.form- control(type='text',name='comment[content]', placeholder='Yorum')
button.btn.btn- default(type='submit') Ekle
-comments.forEach(function(comment) {
.well
p
b=comment.content
p=comment.email
-})
Also i checked my mongodb. There is data. I dont know why 'title' property is 'undefined' have no idea.
This is a race condition issue. The two functions that pull from MongoDB are asynchronous and so the call to res.render() happens before the DB returns the data in each function's respective callback. You need to nest each function so that they have access to the proper context. See below:
app.get('/Post/:id', function (req, res, next){
Post.findOne({_id:req.params.id},function(err, postData){
if (err) return next(err);
Comment.findOne({postid:req.params.id},function(err, comments){
if (err) return next(err);
return res.render(__dirname+"/views/postdetail",{
title: 'adfasdf',
stylesheet: 'postdetail',
post:postData,
comments:comments
});
});
});
});
However, you can see how this can get pretty messy as you nest further and further. To prevent this, you can use a control flow library like caolan/async
Side Note:
You're Jade is looking to iterate over a comments array and you are returning a single doc from MongoDB (assuming you are using the mongoose module). You will want to change your Mongoose function from findOne() to simply find() so that mongoose can return an array of docs with the proper postid.
Edit:
Vinayak Mishra is also correct in pointing out that you can use Express' routing middleware as a way to impose control flow within a route. Here is an example:
// Use the app.param() method to pull the correct post doc from the database.
// This is useful when you have other endpoints that will require work on
// a Post document like PUT /post/:postid
app.param('postid', function (req, res, next, id) {
Post.findById(id, function (err, post) {
if (err) return next(err);
if (!post) return next('route');
req.post = post;
});
});
app.get('/post/:postid',
// -- First route middleware grabs comments from post doc saved to req.post
function (req, res, next) {
Comment.find({ postid: req.post.id }, function (err, comments) {
if (err) return next(err);
req.comments = comments;
next();
});
},
// -- This route middleware renders the view
function (req, res, next) {
res.render('/postDetail', {
// ... list locals here ...
});
}
);
Add this in app.js (server.js) or your root file of the app to post req
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Add this both middleware above on your routes
The two routines to fetch data from db are async routines, hence their callbacks are not invoked by the time you fire res.render(). You will need to wait for the queries to return results, then invoke render.
You can make use of middlewares to do fetching of post and comments in sequence before you're ready to render the post.
Here's an illustration;
app.get('/Post/:id', fetchPost, fetchComments, renderPost, handleErrors);
function fetchPost(req, res, next) {
Post.findOne({
_id: req.params.id
}, function (err, docs) {
if (!err && docs) {
console.log('Gönderi bulundu');
req.postdata = docs;
next();
} else {
console.log('Gönderi bulunamadı');
next(err || new Error('No such post: ' + req.params.id));
}
});
}
function fetchComments(req, res, next) {
Comment.findOne({
postid: req.params.id
}, function (err, comments) {
if (!err) {
console.log('Yorum bulundu');
req.postComments = comments || [];
next();
} else {
console.log('Yorum bulunamadı');
next(err);
}
});
}
function renderPost(req, res, next) {
res.locals.post = req.postdata;
res.locals.comments = req.postComments;
res.locals.title = 'adfasdf - anything that fits';
res.locals.stylesheet = 'postdetail';
return res.render(__dirname + '/views/postdetail');
}
function handleErrors(err, req, res, next) {
// You'll get here only if you recieved an error in any of the previous middlewares
console.log(err);
// handle error and show nice message or a 404 page
res.locals.errmsg = err.message;
res.render(__dirname + '/views/error');
}
Let me know if are unable to follow anything in the code above.