Ejs variable persistence? - node.js

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);

Related

how to pass JSON data into pug template without render()?

in my case, the scenario is when i click a button the value of sort, cost range and the type of Cuisine is got through the JS code then i send a post axios request from that and then passing the data from client side to server side ,where i use a controller to get the values of those data, then i send an axios GET request to the Route which is defined in my code to get the information from the database ,the problem which i face is HOW TO SEND the data from my server to frontend pug template because when i use .render() it shows internal server error in my browser but when i use .json() , i can see the output in my console ,so the problem is with my controller function i guess ? so can anyone look into my problem and provide a solution
the controller code is
exports.filter = catchAsync(async (req, res, next) => {
try {
let cost = req.query.cost;
const cuisine = req.body.params.Cuisine;
const Sort = req.body.params.sort;
let paramobj = {};
if (cuisine) {
paramobj.Cuisine = cuisine;
}
if (Sort) {
paramobj.sort = Sort.toString();
}
if (!cost) {
cost = "";
} else {
cost = `?${cost}`;
}
const data = await axios.get(
`http://localhost:3000/api/ver1/restaurant${cost}`,
{
params: paramobj,
}
);
const restaurantinloc=data.data.data
res
.status(200)
.render("restaurantcard", {
restaurantinloc,
});
// .json(restaurantinloc);
} catch (err) {
console.log(err);
}
});
everything works fine when i use res.json(restaurantinloc) but when i use res.render() i shows internal server error and not rendering the page

Can't get 'res.status(200).redirect('')' to work

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.

How do you conduct multiple queries and send all data to a template for rendering in a single res.render?

I have a MongoDB database called Critterpedia that contains 3 collections: fish, bugs, sea creatures. I am trying to query those collections to render the critters that will be leaving this month. Below is my fish collection query that works and renders. How do I conduct the same query for the other two collections but send all the data at once with a single res.render() call?
app.get('/critters', (req, res) => {
let query = {$and: [{fishMonthsNorth: thisMonth}, {fishMonthsNorth: {$ne: nextMonth}}]}
//Find all fish that leave this month
Fish.find(query, (err, thisMonthFish) => {
if (err) {
console.log(err);
} else {
res.render('index', {thisMonthFish : thisMonthFish});
}
});
});
Ideally I would like to:
//Query fish collection
//Query Bugs collection
//Query Sea collection
//res.render('index', {fish : fish, bugs : bugs, sea : sea});
Thank you in advance!
Well, pretty easy, you wouldn't even believe.
Instead of writing a huge Model.aggregation query with $merge and $lookups, you could make 3 .find at once (in parallel), via Promise.all, Promise.allSettled or for await of
const express = require('express');
const router = express.Router();
/**
* Model importing / Collections
*/
const fish_db = require("../../db/fish_db");
const bug_db = require("../../db/bug_db");
const sea_db = require("../../db/sea_db");
router.get('/critters', async function(req, res) {
try {
let response = {};
/**
* You could form / pre-determine more queries, for each of your collection
*/
let query = {$and: [{fishMonthsNorth: thisMonth}, {fishMonthsNorth: {$ne: nextMonth}}]};
/**
* Promise.allSettled is ES2020 feature, you could use Promise.all instead
* or for await of arrayOfPromises
*/
await Promise.allSettled([
fish_db.find(query).then(docs => { Object.assign(response, {fish: docs}) } )
bug_db.find(query).then(docs => { Object.assign(response, {bug: docs}) } )
sea_db.find(query).then(docs => { Object.assign(response, {sea: docs}) } )
])
/** You could add almost anything to response if you want it to */
await res.status(200).json(response);
} catch (e) {
await res.status(404).json(e);
}
});
module.exports = router;
You could also form your array of promises before, like:
let futureFinds = [];
if (need to search for fish) {
futureFinds.push(await fish_db.find(query))
}
if (need to search for bugs) {
futureFinds.push(await bugs_db.find(query))
}
const [result_one, result_two] = await Promise.all(futureFinds)
and then handle them at-once as you see in code above.

Problem with findById to display product detail via their Id

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>

How to return different results for the same query in Express.js with Node.js?

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

Resources