app.get() cannot find and show the right template(jade) page - node.js

I am studying about very basic Serverside JS using express, jade, node.JS.
At the first page (localhost/topic), there is a list. And List's data from MySQL.
(MySQL fields are id, title, description, and author)
Also, there is a link to open the form (the form is in add.jade file) that can add an item on the list.
My problem is that if I clicked the link, the page still shows the view.jade. But, the address is changed to "localhost/topic/add".
I've checked the "app.js" file to see if app.get() isn't set appropriately. But I did not find any.
Actually, the template was written as jade at first. But I tried to convert jade to pug. After this error, I put them back all to jade.
Is that a problem?
Thank you.
var express = require("express");
var app = express();
app.set("views", "./views_mysql");
app.set("view engine", "jade");
app.listen(3000, function() {
console.log("Connected, 3000 port!");
});
app.locals.pretty = true;
var mysql = require("mysql");
var conn = mysql.createConnection({
host: "localhost",
user: "jimin",
password: "****",
database: "o2"
});
conn.connect();
app.get(["/topic", "/topic/:id"], function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
var id = req.params.id;
if (id) {
var sql = "SELECT * FROM topic WHERE id=?";
conn.query(sql, [id], function(err, id_topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("view", { topics: topics, topic: id_topics[0] });
}
});
} else {
res.render("view", { topics: topics });
}
});
});
app.get("/topic/add", function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("add", { topics: topics });
}
});
});
<!-- begin snippet: js hide: false console: true babel: false -->
// add.jade
doctype html
html
head
meta(charset='utf-8')
body
h1
a(href='/topic') Server Side JavaScript
ul
each topic in topics
li
a(href='/topic/' + topic.id)= topic.title
article
form(action='/topic/add' method='post')
p
input(type='text' name='title' placeholder='title')
p
textarea(name='description' placeholder='description')
p
input(type='text' name='author' placeholder='author')
p
input(type='submit')
//view.jade
doctype html
html
head
meta(charset='utf-8')
body
h1
a(href='/topic') Server Side JavaScript
ul
each topic in topics
li
a(href='/topic/' + topic.id)= topic.title
article
if topic
h2= topic.title
= topic.description
div= 'by ' + topic.author
else
h2 Welcome
| This is Server Side JS Tutorial
div
a(href='/topic/add') add Topic
app.get("/topic/add", function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("add", { topics: topics });
}
});
});

This is an issue with the arrangement of the routes.
TLDR
Move the route definition of app.get above the previously declared route.
Explanation
Route definitions in express are from a top-to-bottom approach. So, in runtime, your routes are seen as follows:
/topic
/topic/:id
topic/add
When you visit the page /topic/add, you would expect it to match to the third on the list but Express actually matches it to the second on the list... It will actually match everything if you put anything after /topic.

Related

Passing mongodb data into views through express

I am building a VERY basic application to book a vehicle for hire, I am trying to access data from mongodb, which seems to work, as it outputs to the console on load. I cannot get this information to render in my view. If I set the render method outside of the function scope I cannot access the db data, if I set the render inside then the booking page never loads. I have been messing around with the same block of code for days now and I have had no joy.
//------------------------------------------------THE SET UP--------------------------------------------
// set node dependencies
let express = require("express");
let bodyParser = require("body-parser");
let mongoose = require("mongoose");
let connection = mongoose.connection;
let data = mongoose.connect("mongodb://localhost:27017/test")
// allow for encoding od POST DATA
let urlencodedParser = bodyParser.urlencoded({extended: false});
// set up app to extend express
let app = express();
// view engine
app.set("view engine", "ejs");
// static files
app.use("/assets", express.static("assets"));
//------------------------------------------------------------------------------------------------------
// REDIRECTS BASED ON URL
app.get('/', function(req,res){
res.render("index")
});
app.get('/booking', function(req,res){
res.render("booking",{qs:req.query})
});
app.post('/booking',urlencodedParser, function(req,res){
// Surround this with if !blacklisted to run inner code
if (req.body.blacklist !== "on"){
console.log(req.body);
// booking page takes age details and redirects/queries database accordingly
if (req.body.age >= 25){
connection.once('open', function () {
connection.db.collection("vehicles", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data[0]._id); // it will print collection data
})
});
res.render("contact-success",{data:req.body})
connection.close();
});
}
else if (req.body.age < 25 && req.body.age > 17){
connection.once('open', function () {
connection.db.collection("vehicles", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data[0]._id + "<25 message"); // it will print collection data
})
})
})
// THIS IS WHERE I WANT TO PASS THE DB DATA INTO.. so that it redirects to this page and filters the vehicles collection appropriately.
res.render("contact-failed",{data:req.body});
}
}
else{
console.log(req.body.firstName , req.body.lastName , "Has been blacklisted!")
res.render("blacklisted",{data:req.body});
}
// else if blacklisted redirect to a sorry, contact company page.
});
let port = 3000;
app.listen(port);
console.log("listening on port " + port);
Few things about your code -
You should call res.render() inside the call to mongodb.
connection.once('open', function () {
connection.db.collection("vehicles", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data[0]._id); // it will print collection data
res.render("contact-success",{data:req.body})
});
});
connection.close();
});
You are not checking for errors. If you get any error with query, you will end up with no response to the request. So it's always better to check for errors.
if(err) {
// process the error and send appropriate message.
} else {
// send what you want to send to view.
res.render("contact-success",{data:req.body})
}
You have conditions for
if (req.body.age >= 25){
...
} else if (req.body.age < 25 && req.body.age > 17){
...
}
But there is nothing for plain else. So if any of the two conditions is not met, you will end up with no response to the request.
There should be one res.render() or res.send() in each of the conditions.

Empty result when querying MongoDB database in Node.JS

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
)

LinkedIn get people by Id in node.js

I want to get some data from an API of LinkedIn with Node.js.
I followed this tutorial https://www.npmjs.com/package/node-linkedin and I wrote this program that is suposed to send data to the console in the callback.
var Linkedin = require('node-linkedin')('XXX', 'XXX', 'http://localhost:3000/oauth/linkedin/callback');
var express = require('express');
var app = express()
// Initialize
var scope = ['r_basicprofile', 'r_emailaddress'];
var linkedinVariables = {
'accessToken': null,
'client': null
}
app.get('/oauth/linkedin', function(req, res) {
// This will ask for permisssions etc and redirect to callback url.
Linkedin.auth.authorize(res, scope);
});
app.get('/oauth/linkedin/callback', function(req, res) {
Linkedin.auth.getAccessToken(res, req.query.code, req.query.state, function(err, results) {
if (err)
return console.error(err);
console.log(results);
linkedinVariables.accessToken = results.access_token;
console.log("ACCESS TOKEN IS ", linkedinVariables.accessToken);
linkedinVariables.client = Linkedin.init(linkedinVariables.accessToken);
/* linkedinVariables.client.people.me(function(err, $in) {
console.log($in);
});*/
/*linkedinVariables.client.people.me('linkedin_id', ['id', 'first-name', 'last-name'], function(err, $in) {
// Loads the profile by id.
console.log($in);
});*/
linkedinVariables.client.people.id('HM3nX8nJD6', function(err, $in) {
console.log($in)
});
// return res.redirect('/');
});
});
app.listen(3000);
Now this program works fine and I get the data with this line:
linkedinVariables.client.people.me('linkedin_id', ['id', 'first-name', 'last-name'], function(err, $in) {
// Loads the profile by id.
console.log($in);
});
which get me a JSON response in my console but following the tutorial I'm suposed to get other informations about companies and people by ID but the response is blank even when I put my own ID to get my own information.
Is there something wrong with my code or is LinkedIn refusing all get requests?
use this
var scope = ['r_basicprofile', 'r_fullprofile', 'r_emailaddress', 'r_network', 'r_contactinfo', 'rw_nus', 'rw_groups', 'w_messages'];
insted of
var scope = ['r_basicprofile', 'r_emailaddress'];

index.jade is not appearing on localhost

I have been following this tutorial on building a web app and database using node.js, express and jade.
https://cozy.io/en/hack/getting-started/first-app.html
Despite laying out everything the same, my index.jade is not loading on localhost. There are no errors to suggest why on browser terminal. I have checked that my environment variables are set up, and I have altered the filepaths to index.jade, but it has not made any difference, just a white screen. On my command prompt, the server is listening and the database is connected.
My environment folder is
C:\foodshop and within this I have
node_modules,
index.jade,
package.json,
shopDB.db,
simpleserver.js
simpleserver.js contains the following -
// This is the server.
var http = require('http'),
express = require('express'),
app = express(),
sqlite3 = require('sqlite3').verbose(),
db = new sqlite3.Database('shopDB.db', (function(err) {
if (!err) {
console.log("Database is connected ... \n\n");
} else {
console.log("Error connecting database, check for shopDB.db file... \n\n");
}
}));
/* We add configure directive to tell express to use Jade to
render templates */
app.get('env', (function() {
app.set('views', __dirname + '');
app.engine('.html', require('jade').__express);
// Allows express to get data from POST requests
app.use(express.bodyParser());
}));
// Database initialization (First list names of tables and check if currently exists.)
db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='itemList'", function(err, row) {
if (err !== null) {
console.log(err);
} else if (row == null) {
db.run('CREATE TABLE IF NOT EXISTS "itemList" ("ID" INTEGER PRIMARY KEY NOT NULL, "itemName" VARCHAR(100) NOT NULL, "itemWeight" INT(5) NOT NULL, "expiryDate" DATE, "itemPrice" double DEFAULT NULL)', function(err) {
if (err !== null) {
console.log(err);
} else {
console.log("SQL Table 'itemList' initialized.");
}
});
} else {
console.log("SQL Table 'itemList' already initialized.");
}
});
// We render the templates with the data
app.get('/', function(req, res) {
db.all('SELECT * FROM itemList ORDER BY itemName', function(err, row) {
if (err !== null) {
res.send(500, "An error has occurred -- " + err);
} else {
//res.sendfile('./public/index.html')
res.render('./index.jade', {
itemList: row
}, function(err, html) {
//res.sendfile('./index.jade')
//res.send(200, html);
res.status(200).send(html);
});
}
});
});
// We define a new route that will handle item creation
app.post('/add', function(req, res) {
ID = req.body.ID;
itemName = req.body.itemName;
itemWeight = req.body.itemWeight;
expiryDate = req.body.expiryDate;
itemPrice = req.body.itemPrice;
sqlRequest = "INSERT INTO 'itemList' (ID, itemName, itemWeight, expiryDate, itemPrice) VALUES('" + ID + "', '" + itemName + "', '" + itemWeight + "', '" + expiryDate + "', '" + itemPrice + "')"
db.run(sqlRequest, function(err) {
if (err !== null) {
res.send(500, "An error has occurred -- " + err);
} else {
res.redirect('back');
}
});
});
// We define another route that will handle item deletion
app.get('/delete/:itemName', function(req, res) {
db.run("DELETE FROM itemList WHERE itemName='" + req.params.itemName + "'", function(err) {
if (err !== null) {
res.send(500, "An error has occurred -- " + err);
} else {
res.redirect('back');
}
});
});
/* This will allow Cozy to run your app smoothly but
it won't break other execution environment */
var port = process.env.PORT || 9250;
var host = process.env.HOST || "127.0.0.1";
// Starts the server itself
var server = http.createServer(app).listen(port, host, function() {
console.log("Server listening to %s:%d within %s environment",
host, port, app.get('env'));
});
And this is the index.jade file
doctype 5
html(lang="en")
head
title Items
body
form(action="add", method="post")
label ID:
input(type="text", name="ID")
label itemName:
input(type="text", name="itemName")
label itemWeight:
input(type='text', name='itemWeight')
label expiryDate:
input(type='text', name='expiryDate')
label itemPrice:
input(type='text', name='itemPrice')
input(type="submit", value="Add a new item")
ul
- for(item in itemList) {
li
a(href=itemList[item].url)= itemList[item].itemName
| - (
a(href="delete/#{itemList[item].id}") delete
| )
- }
I followed that same tutorial and got to the part where they start with the Jade template and had the exact same problem you did. I backed up a little bit, and grabbed the example template from the Jade website, and it worked fine. I changed Cozy's "bookmark" template a little bit and got it working. You might try this:
doctype html
That different doctype made a difference in my example. I'm not an expert on Jade, but I'd definitely try that and see if you have any luck.
Edit: After looking a bit further, it looks like doctype 5 is deprecated, and doctype html is recommended now.
Another Edit: If you're still having issues with your view rendering, I'd do two things. One, I'd check the tutorial out, match their jade view with yours, and start adding things one at a time until it breaks to narrow down the issue. Two, I'd change:
app.get('env', (function() {
app.set('views', __dirname + '');
app.engine('.html', require('jade').__express);
// Allows express to get data from POST requests
app.use(express.bodyParser());
}));
to
app.set('views', __dirname + '');
app.engine('.html', require('jade').__express);
// Allows express to get data from POST requests
app.use(express.bodyParser());
You don't appear to be using the env. variables, and I don't see any reason to move your view renderer setup inside that. Move it to the top (like in the example) and see if that works for you.

How to send the number of models inside a collection, from Nodejs and Express, over to Backbone

I use nodejs and express for my js server.
I need a way to fetch the number of models inside a collection and send it to Backbone.
The express side is rather straight forward:
db.books.count(function (err, booksNr) {
if (err) return;
console.log(booksNr)
res.json(booksNr);
});
But how do i send this over to backbone?
My first thought was to bing the booksNr to a collection, but I can't make it work.
Here is how I fetch a collection from Backbone:
books.fetch(
{
data: {
'fetchType' : 'list',
'currentPage': 1,
'perPage': 3
},
success: function(books) {
console.log(books);
var books = books.models;
var template = _.template(listBooksTpl, {
books : books,
});
setTimeout(function () {
that.$el.html(template);
$(that.preLoader).hide();
that.$el.show('slow');
}, 500);
},
error: function() {
console.log("BookList error!");
}
}
);
Here is how I send the collection data to Backbone:
exports.books.paginated = function (req, res) {
var currentPage = parseInt(req.query.currentPage - 1),
perPage = parseInt(req.query.perPage),
skip = parseInt(currentPage * perPage);
db.books.find({}).limit(perPage).skip(skip, function (err, books) {
if (err) return;
res.send(books);
});
};
Any ideas?
It is quite simple, you need an api for retrieving books, in express.js:
app.get('/api/books', function(req, res){
var query = createQueryFromReq(req);
BookModel.find(query).exec(function(err, books) {
res.send(books);
});
});
Then you need a Books Backbone.Collection in your client side, notice that it does the parsing automatically, you don't need to add your own success parser:
var Book = Backbone.Model.extend({});
var Books = Backbone.Collection.extend({
model:Book,
url: "/api/books",
});
// Some main.js
var myBooks = new Books();
myBooks.fetch({
data: {/* your data */},
reset: true // Trigger reset in backbone 1.0+
});
Now, you should listen to "reset" event fired on myBooks and act upon it
After Comments:
The need is to be able to paginate from the server, I would recommend using https://github.com/backbone-paginator/backbone.paginator
if you want to use your own solution, you could be using this approach, on the Server side supply the extra information:
app.get('/api/books', function(req, res){
var limit = req.params.limit;
var offset = req.params.offset;
var bookCount = getBookCount(); // how many books we have
BookModel.find(query).skip(offset).limit(limit).exec(function(err, books) {
res.send({
limit: limit,
offset: offset,
total: bookCount,
books:books,
});
});
});
On the Client side you should use the parse function on Books:
var Books = Backbone.Collection.extend({
model:Book,
url: "/api/books",
parse: function(response){
this.total = response.total;
this.offset = this.offset + this.books.length; // I would test this line :) it might need a -1 also here
return response.books; // only using the books part of the response to create the models
}
});
Now you need to keep track on your limit and offset, and in the first time you fetch use data give it offset zero:
myBooks.fetch({
data: {offset:0, limit:10},
});
Another alternative it to have this Collection part of a Paginator Model, (but you can just use backbone.paginator for this)
Upon more research, here is how I did it...
Is this a good way to go?
Is it a good practice to fet a model without an ID?
In Backbone, I instantiate a model without any id:
// Random model - get total number of books.
book = new Book();
// Get the total number of books.
book.fetch({
data : { 'fetchType' : 'booksNr' },
success : function (booksNr) {
that.booksNr = booksNr.get('booksNr');
},
error : function () {
console.log("Error! Failed to get the total number of books.");
}
});
In express, I look for the query string: fetchType and do the following:
exports.books.paginated = function (req, res) {
var fetchType = req.query.fetchType;
if (fetchType == 'booksNr') {
db.books.count(function (err, booksNr) {
if (err) return;
res.json({ 'booksNr' : booksNr });
});
}
};

Resources