Passing MongoDB Data into .ejs-Template with Node.js Express - javascript

I think i clicked myself through thousands of tutorials but i'm still stucked at this point: I want to render all the data, which my express-app is writing into mongodb at its start, into embedded javascript. I would like to have a simple table which is showing all the data from mongodb. It shall also always get the actualized Data when calling the route.
My first idea was, to save the data in an array. Pass it to the .ejs file. Create a table, iterate through my data-array and write it in. My problem is, that i can not write the data into an array after calling the find()-Function.
The model subscriber.js:
const mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var subscriberSchema = mongoose.Schema({
nr: Number,
mailIdent: {
type: String,
unique: true
},
from: String,
emails: {
type: String,
default: ''
},
text: String,
uLink: String,
anwalt: Boolean,
create_date:{
type: Date,
default: Date.now
}
});
subscriberSchema.plugin(uniqueValidator);
var Subscriber = module.exports = mongoose.model('Subscriber', subscriberSchema);
I'm really new to the topic and it feels like i'm just messing around. Please help
//get Subscriber
/*module.exports.getSubscribers = Subscriber.find(function(err, subs){
if(err) return console.error(err);
console.log(subs);
});
*/
module.exports.subscriber = Subscriber;
module.exports.getSubscriberByID = function(_id, callback){
Subscriber.findById(_id, callback);
};
module.exports.getSubscribers = function(){
var subscribers = Subscriber.find({});
return subscribers;
};
Then i want to pass it with my app.js to the index.ejs:
app.get('/', function(req, res){
var subs = Subscriber.getSubscribers().toArray();
console.log(subs);
res.render('index',{subs: subs} );
});
I know, that my .ejs still seems a little simple. But so far it shall be just functional:
<!DOCTYPE html>
<html>
<head>
<link href="/assets/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<% include partials/nav.ejs %>
<h1>Welcome to the Database</h1>
<p>You won't find more Information than here!</p>
<p>Table</p>
<table>
<colgroup span="5" class="columns"></colgroup>
<tr>
<th>Nr</th>
<th>Name</th>
<th>Mail</th>
<th>uLink</th>
<th>Anwalt</th>
</tr>
<% for (var i = 0; i<subs.length; i++) { %>
<tr>
<td><%= subs[i].nr</td>
<td><%= subs[i].name</td>
<td><%= subs[i].email</td>
<td><%= subs[i].uLink</td>
<td><%= subs[i].anwalt</td>
</tr>
<% } %>
</table>
</body>
</html>

The following is from mongoose docs:
Query#find([criteria], [callback])
When no callback is passed, the
query is not executed. When the query is executed, the result will be
an array of documents.
You can use a callback just Like you do with getSubscriberByID function, here is an example:
subscriber.js:
...
module.exports.getSubscribers = function(cb){
Subscriber.find({}, cb);
};
app.js
app.get('/', function(req, res){
Subscriber.getSubscribers( function (err, subs) {
if (err) throw err;
// else render result
res.render('index', { subs: subs} );
});
});

here is ur app.js code..
app.get('/', (req, res) => {
// db.collection('story').aggregate([
// { $lookup:
// {
// from: 'story_content',
// localField: 'ObjectId("5a322e1130cb6225a086f37d")',
// foreignField: "5a322e1130cb6225a086f37d",
// as: 'joinstorydata'
// }
// }
// ]).toArray(function(err, res) {
// if (err) throw err;
// console.log("********************************************************")
// console.log(res);
// final=res;
// });
db.collection('bid_placement').find().toArray((err, docs2) => {
if (err) return console.log(err)
// renders index.ejs
lnames2 = [...new Set(docs2.map(a => a.bid_location))]
lnames2.sort();
res.render('index.ejs', {
//story12 : docs1 ,
//story_content: final,
storylocation : lnames2
});
});
});
and here is ur html code
<select name="Courses" id="Courses">
<% for(var i=0; i<storylocation.length; i++) {%>
<option value="<%= storylocation[i]%>"> <%= storylocation[i]%> </option>
<% } %>
</select>
you can use it like <%= storylocation[i].abc%> .. put you desire data instead of abc on each column of table...

It was driving me mad and finally i found out. I did not closed the Javascript in the .ejs file. That was definetly the most stupid misstake ever

Related

Mongoose not updating database with findByIdAndUpdate?

I am trying to update my database using findByIdAndUpdate through an HTML form, which works for all but the nested data (ePIMS, codeVersion, and region all update with no problem). When I do console.log(req.body.environment.instance), it outputs the data I typed in like it's going through correctly, but for some reason the mongoDB does not update with the information. Can anyone figure out what I'm doing wrong?
mongoose schema:
var environmentSchema = new mongoose.Schema({
ePIMS: String,
codeVersion: String,
region: String,
/*instance and testEnv don't seem to update in the database*/
HCHC: {
instance: String,
testEnv: String
}
});
form I'm using to update:
<form action="/environments/<%= environment._id %>?_method=PUT" method="POST">
<input class="form-control" type="text" name="environment[ePIMS]" placeholder="ePIMS" value="<%= environment.ePIMS %>" />
<input class="form-control" type="text" name="environment[region]" placeholder="Region" value="<%= environment.region %>" />
<input class="form-control" type="text" name="environment[instance]" placeholder="HCHC Instance" value="<%= environment.instance %>" />
<input class="form-control" type="text" name="environment[testEnv]" placeholder="Test Environment" value="<%= environment.testEnv %>" />
<button class="btn btn-primary">Submit</button>
</form>
edit and update routes:
//Edit environment route
router.get("/environments/:id/edit", function(req, res){
Environment.findById(req.params.id, function(err, foundEnvironment){
if(err){
res.redirect("/");
} else {
res.render("edit", {environment: foundEnvironment});
}
});
});
//Update environment route
router.put("/environments/:id", function(req, res){
Environment.findByIdAndUpdate(req.params.id, req.body.environment, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
//console.log(req.body.environment.instance)
}
});
});
UPDATE: SOLUTION
Thank you Nayan for your help!
I changed the update route like so:
//Update environment route
router.put("/environments/:id", function(req, res){
var data = {
HCHC : {
instance: req.body.instance,
testEnv: req.body.testEnv
}
}
Environment.findByIdAndUpdate(req.params.id, {$set: data}, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
}
});
});
You are sending different body structures and setting it directly in findOneAndUpdate() so it didn't work, where the structure is different.
The possible solution you can apply is to change the body of the request to look something like this:
"environment" : {
"ePIMS" : value,
"codeVersion" : value,
"region" : value,
"HCHC": {
"instance" : value,
"testEnv" : value
}
}
Or you can put the two thing instance and testEnv out of HCHC if you want.
Either way make sure you have same structure if you are passing the body directly in the function.
Another solution
create a local variable to fix the structure and pass that in function like:
router.put("/environments/:id", function(req, res){
let body = req.body.environment
let bodyData = {
ePIMS: body.ePIMS,
codeVersion: body.codeVersion,
region: body.region,
HCHC: {
instance: body.instance,
testEnv: body.testEnv
}
}
Environment.findByIdAndUpdate(req.params.id, bodyData, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
//console.log(req.body.environment.instance)
}
});
});
this solution worked for me:
router.patch('/tasks/:id', async (req,res)=>{
try{
const task= await Task.findByIdAndUpdate(req.params.id,req.body,{new:true,runValidators:true})
if(!task)
{
res.status(404).send()
}
res.send(task)
}
catch(e)
{
res.status(500).send()
}
})
however,my final solution included validation was inplementing update without using findByIdAndUpdate:
router.patch('/tasks/:id', async (req,res)=>{
//validate update operation
const updates=Object.keys(req.body)
const allowedUpdates= ['description','completed']
const isInvalidOperation= updates.every((update)=>allowedUpdates.includes(update))
if(!isInvalidOperation)
{
return res.status(400).send({error:'invalid updates'})
}
try{
const task= await Task.findById(req.params.id)
updates.forEach((update)=>task[update]=req.body[update])
await task.save()
if(!task)
{
res.status(404).send()
}
res.send(task)
}
catch(e)
{
res.status(500).send()
}
})

Why is this app.locals function in a button not working on my EJS template?

I have this script agents.js as follows
var express = require('express');
var router = express.Router();
var app = express(); // is it wise to call express like this twice?
router.get('/', function(req, res, next) {
var query = "role=agent";
get('employees').then(function(data) {
res.render("agents", {
rows: result,
user: req.user
});
}, function(error) {
res.send(JSON.stringify(error));
});
});
/// how can I expose this function to the application properly?
app.locals.add2Users = function(item) {
console.log("ADDING SOMEONE");
api.create(item, 'employees').then(function(data) {
console.log("Nice");
}, function(error) {
console.log("Error");
});
};
module.exports = router;
And a simple template agents.ejs
<% for(var i=0; i < rows.length; i++) { %>
<tr>
<td>
<%= rows[i].name %>
</td>
<td>
<%= rows[i].email %>
</td>
<td>
<% if (rows[i].toAdd) { %>
<button type="button" class="btn btn-info btn-outline btn-circle btn-lg m-r-5" onClick="add2Users(rows[i])">
<i class="ti-magnet"></i>
</button>
<% } %>
</td>
</tr>
<% } %>
The goal is to create a user on some back end database when the button on the template is clicked, by calling the add2Users function. I read that you can expose a function on an EJS template by adding it to app.locals, however I am getting the error add2Users is not defined. What am I doing wrong and what is the best way to expose a script function on an EJS template without going against best practices so to speak?
Rather than using app.locals, I ended up creating an endpoint /addUser/{param} and calling the endpoint from the client on click of the button.

Display to client only if collection exists on mongoDB Node JS

I have this schema model defined on Mongoose:
var mongoose = require("mongoose");
var IngredientSchema = new mongoose.Schema({
name:String,
number:Number,
exist:Boolean,
photoName:String
})
module.exports = mongoose.model("Ingredient", IngredientSchema);
And I want to display on a web page a different result depending on whether there is an Ingredient already created on database or not.
Here's what I tried so far (but it doesn't work):
<!-- Check if inventory is empty or not, and display accordingly -->
<% if ( ! ingredients) { %>
<p>Add you first ingredient to the inventory !!</p>
<% } else { %>
<% ingredients.forEach(function(ingredient) { %>
...
...
And here's my route:
// Index route
app.get("/inventory", function(req, res) {
Ingredient.find({}, function(err, allIngredients) {
if (err) {
console.log(err);
} else {
res.render("inventory", { ingredients:allIngredients });
}
})
})
Thank you very much for your help.
Just check the length of ingredients array:
<!-- Check if inventory is empty or not, and display accordingly -->
<% if (!ingredients.length) { %>
<p>Add you first ingredient to the inventory !!</p>
<% } else { %>
<% ingredients.forEach(function(ingredient) { %>
...
...

Node.js cant log Class model data from Student model

I am having an issue here. I currently am making a school directory using node.js and MongoDB. I am in an app.post request and for some reason I can't get the name of the class being linked to the student to log to the console, but createdClass.name will print...
Here is the code...
app.post("/students/:id", function(req, res){
Student.findById(req.params.id, function(err, foundStudent){
if(err){
console.log(err);
} else {
Class.create(req.body.class, function(err, createdClass){
if(err){
console.log(err);
} else {
createdClass.student.id = foundStudent._id;
createdClass.student.name = foundStudent.name;
console.log(createdClass);
createdClass.save();
foundStudent.classes.push(createdClass);
console.log(foundStudent.classes[0].name);
foundStudent.save();
}
});
}
});
res.redirect("/students/" + req.params.id);
});
Also, here are my models...
STUDENT:
var mongoose = require("mongoose");
var studentSchema = new mongoose.Schema (
{
name: String,
classes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Class"
}
],
grades: Array
}
);
module.exports = mongoose.model("Student", studentSchema);
CLASS:
var mongoose = require("mongoose");
var classSchema = new mongoose.Schema (
{
name: String,
student:
{
id:
{
type: mongoose.Schema.Types.ObjectId,
ref: "Student"
},
name: String
}
}
);
module.exports = mongoose.model("Class", classSchema);
Thank you in advance and please do let me know if there is anything I can add to make this easier to read.
Here is the page making the post request...
<div>
<h1>Student Profile</h1>
<h2>Name: <%=student.name%></h2>
<div>
<h3>Classes:
<form action="/students/<%= student._id %>" method="POST">
<%if(student.classes.length === 0){%>
<p>No classes linked to profile, please add class..</p>
<input type="text" name="class[name]" placeholder="Class name">
<% } else { %>
<% student.classes.forEach(function(course){ %>
<li><%= course.name %></li>
<% }); %>
<% } %>
</form>
</h3>
</div>
</div>
Class is a reserved word and can't be used for a variable

Send value from backend to frontend in Sails.js and check with EJS

I'm trying to submit data from a form, then from the controller there are 3 possibilities, it already exists, the data is wrong and it was successfully created. Then I want to return a value to the front-end, let's say everything is ok and I return {result: 0}.
Then using ejs I'll check the value of 'result' and if it's a 0 I want to call a (front-end) javascript function.
EJS:
<html>
<head>
<script src="functions.js">
</head>
<body>
<form action="/test" method="POST">
<input type="text" name="data" />
<button>Send</button>
</form>
<div id="message">
<% if (typeof result !== 'undefined' && result === 0){ %><script>showMessage();</script><% } %>
</div>
</body>
</html>
Javascript (front-end)
function showMessage() {
document.getElementById('message').innerText = "sometext";
}
Javascript (back-end)
module.exports = {
postData: function (req, res) {
Data.findOne({data: req.param('data')}).exec(function (err, data) {
if (err) {
return {result : 1};
}
if (data) {
return {result: 2};
}
if (!data) {
Data.create({data: req.param('data')}, function (err, data) {
if (err) {
return {result: 1};
}
return {result: 0};
});
}
});
}
}
I know how to send data using res.view(), but I don't want to reload the page again I just want to send something to the frontend and make some action.
Thank you for your time.
Your best bet is to include the Javascript script you want to execute inline. Then, you can use a conditional EJS clause to include your script if the result is equal to 0.
<% if (result == 0) { %>
<script type="text/javascript">
/* Your Function Here */
</script>
<% } %>
You can store variables in the backend eg. someVariable = "some value"
Then in your frontend call <%- someVariable %>
OR save it as an object in the backend eg.
vehicle.car = {
manufacturer: 'Holden',
make: 'Colorado',
year: '2018'
}
THEN in the frontend call the object model eg.
<p>Your <%- vehicle.car.year %> <%- vehicle.car.manufacturer %> <%- vehicle.car.make %>...
A use case example is to show the current year of copyright of a footer partial that persist page requests.

Categories