I am new to nodeJs and wanted to practice creating a shopping cart. I all succeeded alone but managed to display the detail of products clicked by the user that it does not work:
SUMMARY OF MY CODE
I created in my models folder two js files called user.js and jacket.js which each contain a schema and model separately. In my Controllers folder, I have an index.js file which includes:
const User = require ("../models/users")
const Jacket = require ('../models/jacket')
My question is that I want to access the Id of these two documents at the same time: Here is my code
const getProductId = (req, res, next) => {
const id = req.params.id;
const Users = User;
const Jackets = Jacket;
!!!. findById(id, (error, product) => {
if (error) console.log(error);
console.log(product);
res.render("product-detail", {
product: product
});
});
};
module.exports = { getProductId: getProductId };
My routes folder:
const express = require ('express')
const router = express.Router ()
const getIndexController = require ('../controllers/index')
router.get ('/product/:id', getIndexController.getProductId)
THANKS TO ALL
By default mongodb autogenerates ids, so having the same id in two different collections seems unlikely unless you customized the ids.
But anyway... Here is a promise-based solution.
Promise.all([
User.findById(id).exec(),
Jacket.findById(id).exec()
])
.then(([user, jacket]) => {
// Or however your template might look like
res.render('product-detail', { user, jacket } )
})
.catch(error =>{
console.log(error)
})
The key here is that mongo will not wait after it sends a query to the database unless you force it to.
If you insist on using callbacks then you have to chain them:
User.findById (id, (error, user) => {
Jacket.findById (id, (error, jacket) => {
// This is what we call "callback hell"
// It might not seem so bad, but it quickly builds up.
i tried that
const getProductId = (req, res, next) => {
const id = req.params.id Promise.all ([ User.findById (id) .exec (), Jacket.findById
(id) .exec ()])
.then (([product]) => { res.render ('product-detail', {product}) })
.catch (error => { console.log (error) }) }
it only works for User
Here is my code in my ejs file
<h1> <% = product.name%> </h1>
<img width = "200px" height = "200px" src = "<% = product.image%>" alt = "<% =
product.image%>" />
<h1> <% = product.description%> </h1> <h1> <% = product.prix%> </h1>
Related
I am working on a project that requires me to redirect the user to a new page after a mysql query. For some reason it works throughout the rest of the program but in this particular section, it just does nothing.
Here is my code that doesn't work.
No idea what im missing here...
exports.ad_dash_load = async (req, res) => {
var uid=req.body.User_id;
db.query('SELECT COUNT(*) AS `z`FROM `user`', async (error, results)=>{
if(error) {
console.log(error);
} else {
let user_count=results[0].z;
res.status(200).redirect("/admin?Id="+uid+"&user_count="+user_count);
}
})
}
So there are 2 other ways we can execute this query
OPTION 1
Save the result into a variable that comes from the query and then check if the var has the result
exports.ad_dash_load = async (req, res) => {
var uid = req.body.User_id;
const result = await db.query("SELECT COUNT(*) AS `z`FROM `user`");
if (!result) {
console.log(error);
} else {
let user_count = results[0].z;
res.status(200).redirect("/admin?Id=" + uid + "&user_count=" + user_count);
}
};
OPTION 2
Using this method is equivalent to using try catch block
Use .then().catch() block on the async db query
exports.ad_dash_load = async (req, res) => {
var uid = req.body.User_id;
db.query("SELECT COUNT(*) AS `z`FROM `user`")
.then((result) => {
let user_count = results[0].z;
res
.status(200)
.redirect("/admin?Id=" + uid + "&user_count=" + user_count);
})
.catch((err) => {});
};
Ok i think i understand the root of this problem....the "exports" function is supposed to be triggered from a Form POST on a previous page but because i was not moving from a page with a form i opted to use a javascript routine to send user id code to the "exports" function. This fact is responsible for the redirect not working. Trying to figure out another way around this.
I have a NODE.JS api using expressjs that connects to an SQL Server, and I want to use it in an angular project. I make use two files, a route file and a controllers file. My route file is as follows:
module.exports = (app) => {
const UsrContrllr = require('../Controllers/users.controllers');
//1. GET ALL USERS
app.get('/api/users', UsrContrllr.func1);
//2. POST NEW USER
app.post('/api/user/new', UsrContrllr.func2);
};
And my controllers file is given below:
const mssql = require('mssql');
exports.func1 = (req, res) =>
{
// Validate request
console.log(`Fetching RESPONSE`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `SELECT * FROM USERS`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() === '') {
res.send('Oops!!! Required data not found...');
}
else {
// send records as a response
res.send(recordset);
}
};
});
};
exports.func2 = (req, res) =>
{
// Validate request
console.log(`INSERTING RECORD ${req}`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `INSERT INTO GDUSERS (USERCODE, PASSWORD, LANGUAGE, USERCLASS, FIRSTNAME, LASTNAME, CONTACTNO) VALUES ('${req.body.usercode}', '${req.body.password}', 'EN', '0', '${req.body.firstname}', '${req.body.lastname}', '${req.body.contactno}');`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() == '') {
res.send('Oops!!! Required data not found...');
}
else {
// Send records as response
res.send(recordset);
}
};
});
};
The GET request works well, but when I try to run the POST request directly from the angular application, I get an error stating
Cannot GET URL/api/user/new
The angular code in my angular project is:
signup() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
console.log(this.user); //User details come from a form
this.http.post(“URL", this.user, options)
.subscribe(
(err) => {
if(err) console.log(err);
console.log("Success");
});
}
I’m not sure whether the angular code I’m using, is right or not, and I don’t know where I’m going wrong. How does one exactly send a http POST request from an Angular project?
this i the way i handled my user signup with http.post calls. my approach is slightly different when signing up user because i am using a promise instead of observable (which i normally use for my servicecalls). but i will show you both ways.
createUser(user: User): Promise < string > {
const promise = new Promise < string > ((resolve, reject) => {
const userForPost = this.createUserForPost(user);
this.http.post(environment.backendUrl + '/api/user/signup', userForPost, this.config).toPromise < HttpConfig > ()
.then(createdUser => {
}).catch(error => {
console.log(error);
});
});
return promise;
}
here another example with an observable
createForumPost(forumPost: ForumPost) {
this.http.post < { message: string, forumPostId: string } > (environment.backendUrl + '/api/forumPosts', forumPost).subscribe((responseData) => {
const id = responseData.forumPostId;
forumPost.id = id;
});
}
i defined my URL somewhere else and then just use the environment.backedUrl + 'path' to define my path (the same as the path in your backend controller)
this is one of my first answers here on SO. i am sry if it is a bit messy
i hope i was able to help with my examples :)
I have an express route which filters the month of a blog post and returns the months:
router.get("/months", async (req,res)=>{
try{
let posts = await pool.query(`select * from posts
where MonthName(created_at) = '${req.query.month}'`)
console.log(`select * from posts
where MonthName(created_at) = '${req.query.month}'`)
let tags = await pool.query(`Select tagName from tags`)
filter = req.query.month
res.render("index",{posts,tags,filter})
}
catch(err){
console.log(err)
}
})
It returns the blog posts filter by the month. As you can see I am sending back a filter variable so that in my template I am display the header as in my index.ejs file:
<h2><%= filter %> Blogs</h2>
So that it shows up as April Blogs or whatever filter the user selected
Now the same template is also shated by the default index route:
router.get("/", async (req,res)=>{
try{
let rows = await pool.query(`Select userName as author, posts.* from posts
inner join users on users.id = posts.user_id`)
let tags = await pool.query(`Select tagName from tags`)
res.render("index",{posts:rows,tags:tags})
}
catch(err){
console.log(err)
}
})
By default no filter is applied and a filter variable is not even sent.
Now, the localhost:3000/months/?month=April is perfectly as expected and shows only the blog from April.
But I was expected the localhost:3000 route to throw an error because it is not passing the filter variable but it shows whatever month I selected in the previous route filter.
Only when I terminate the Nodejs server and try to go to the default route do I get:
filter is not defined
But if I go to localhost:3000/months/?month=April and then go back to ``localhost:3000` it loads just fine with month April.
Is it not a good idea to share templates between different routes? How is this possible?
The template is getting cached on first invocation. You might try this answer to disable the view cache in express -
router.disable('view cache');
Another options is listed on the ejs wiki -
router.set('view options', {cache: false});
or you might try disabling in ejs with the additional options parameter -
res.render("index",{posts:rows,tags:tags}, {cache: false})
You need to declare filter and pass a default value, otherwise your renderer will fail.
router.get("/months", async (req,res)=> {
try {
let dbQuery = `select * from posts`;
// handle when req.query.month is not passed.
if (req.query.month) {
dbQuery += ` where MonthName(created_at) = '${req.query.month}'`;
}
const posts = await pool.query(dbQuery);
const dbQuery2 = 'Select tagName from tags';
const tags = await pool.query(dbQuery2);
// declare filter here and assign '' when it's not passed request,
// otherwise your render will fail.
const filter = query.month ? query.month : '';
return res.render("index", {posts, tags, filter});
} catch(err){
// pass error to your main error handler.
return next(err);
}
})
Further i would recommend organising your code better.
// your common shared logic
async function logic({query, params, body}){
const dbQuery = `select * from posts`;
const posts = await pool.query(dbQuery);
return {posts};
}
function handler(req, res, next) {
logic(req)
.then((result) => {
return res.render("index",result);
})
.catch(next);
}
// you can create separate handlers for each requests
// and use the shared logic in handlers.
router.get("/", handler);
router.get("/months", handler);
I'm new to Node.js and Express.js and I'm wondering how to return different results with the same query based on different logics. Here is my code:
const { Student } = require('../mongoose-models/student');
const express = require('express');
const router = express.Router();
router.get('/api/students/', async (req, res) => {
const query = req.query;
const studentsList = await Student.find(query);
if (!studentsList) return res.status(404).send('No student found.');
res.send(studentsList);
});
module.exports = router;
Now, if I go to http://localhost:3000/api/students/age=20 in my browser, then it will return a list of all students that are exactly 20 in a json format (the student data is stored in MongoDB). Now, I want to implement another function that will return a list of students younger than 20 when specifying age=20. Is this possible to add this logic within the same block and how can I do it?
In express for same GET request with dynamic parameter we have
router.get('/api/students/:dynamicVariable', async (req, res) => {
// you can pass multiple parameter in url
const query = req.params.dynamicVariable
// for access then use req.params.paramsNameInUrl with ":" in from of variable
})
This is wrong way to query from database But it help for big logic
router.get('/api/students/query=:Query', async (req, res) => {
const query = req.params.Query;
const studentsList = await Student.find(query);
if (!studentsList)
return res.status(404).send('No student found.');
res.send(studentsList);
});
Your query in Postman or url
www.your-domin.com/api/students/query={"age": 20 }
// or query based on your requirement
query={"status":true}
query={"age": { $gt: 20 } }
query={"age": {$nin: [20, 30, 13]}}
use GET Request if using Post
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: ''
}
},