'Undefined' returned while retrieving mongoose document elements - node.js

I am trying to display my mongodb data in a bootstrap template.
api.js
var Software = require('../models/dbSoftware.js');
exports.show = (function(req, res) {
Software.find({Publisher: req.params.format}, function(error, softwares) {
res.render('searchpage', { searchText: req.params.format, softwares: softwares});
})
});
Software Schema:
var mongoose = require('mongoose');
var softwareSchema = new mongoose.Schema({
SoftwareName: String
, Size: String
, Publisher: String
, Product: String
, Version: String
, Language: String
, License_Type: String
, Description: String
, License_Key: String
});
module.exports = mongoose.model('Software', softwareSchema);
searchpage.ejs
<td><%= softwares.Product %></td>
This returns undefined value. Any softwares.* returns 'undefined'.
When I edit searchpage.ejs to:
<td><%= softwares%></td>
The output is:
{ License_Key: 'computing', Description: 'Licensed', License_Type: 'English', Language: 'UNKNOWN', Product: 'Test Test', Publisher: 'Test', Size: '231.6MB', SoftwareName: 'Test.zip', _id: 5252c407d9b28d1e4c000001, __v: 0 }
//which is correct

When using .find(), softwares will be an Array for all successful (error == null) queries, regardless of the number of documents found:
<%= softwares.length %>
<%= softwares[0].Product %>
If you only want a single document, you'll want to use .findOne() instead:
Software.findOne({Publisher: req.params.format}, function(error, softwares) {
// ...
});
<% if (softwares != null) { %>
<%= softwares.Product %>
<% } %>
Or, you can iterate over softwares:
<% softwares.forEach(function (software) { %>
<%= software.Product %>
<% }) %>

Related

NodeJS/Mongoose - Combining then accessing collections

I have a field called 'allGroups' under my Blogs schema which combines all the options from 3 different models (Community, Business and Help) and allows the user to select multiple options from a dropdown list and store their ID in an array.
allGroups: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Community"
},
{
type: mongoose.Schema.Types.ObjectId,
ref: "Business"
},
{
type: mongoose.Schema.Types.ObjectId,
ref: "Help"
}
],
This is correctly saving the id of the respective option to the database; however, when I try to retrieve the name of each in my .EJS template using a forEach loop, I'm only able to return group._id, whereas I also want to return group.name (all individual models have the 'name' field). So my first question is, do I need to add something else in my model for the allGroups field to be able to access group.name, or is there another way I can access?
<% blog.allGroups.forEach((group) => { %>
<div><%- group._id %> <%- group.name %></div>
<% }) %>
Second question: As well as returning group.name, I also want to link to the respective records, which requires me to access their original models. Below is some mock code as an example; how can I reference the original model that each record belongs to so I can return the correct link prefix for each? I'm aware of Mongoose' 'instanceof' method, but I've only managed to get this to work in my controller, I'm unsure if it's possible to use within my .EJS template directly? Or is there another/better way to do this? Thanks.
<% blog.allGroups.forEach((group) => { %>
<% if (Original Model === "Business") { %>
<div><%- group.name %></div>
<% else if (Original Model === "Help") { %>
<div><%- group.name %></div>
<% else if (Original Model === "Community") { %>
<div><%- group.name %></div>
<% } %>
<% }) %>
Current controller for reference:
async blogShow (req, res, next) {
let blog = await blog.findById(req.params.id).populate('interviewees allGroups likes').populate({
path: "comments",
model: "Comment",
options: { sort: { createdAt: -1 }}
});
res.render("blogViews/showBlog", { blog, cloudinary });
},
You have to render the ejs file here res.render("blogViews/showKnowPangyao" it should be something like blog.ejs or blog:the name of the ejs file, and in place of knowPangyaoyou have to pass the results returned by query that is blog not {knowPangyao, cloudinary } it should be { blog:blog }.
TRY:
async blogShow (req, res, next) {
let blog = await blog.findById(req.params.id).populate('interviewees allGroups likes').populate({
path: "comments",
model: "Comment",
options: { sort: { createdAt: -1 }}
});
res.render("blogViews.ejs", { blog:blog });
},

Search array MongoDB NodeJS

I am struggling to get records where the name matches the name i have stored an another database.
I can retrieve all fields of data but i am struggling to get specific records i.e. where name = either jack or jill.
I want to use a variable i take from my passport log in which i can access at req.user.username.
I tried using findOne and manipulating the ejs array without any luck.
It prints to the console but i cannot display on vieworders.ejs
I think i am overcomplicating, what is the best way to find and display records where applicationDB.name = req.user.username?
server.js is
app.get('/vieworders', require('connect-ensure-login').ensureLoggedIn(), function(req, res) {
Myapplication.find(function(err, myorders) {
res.render('vieworders', { user: req.user, myorders: myorders});
console.log(myorders);
});
});
vieworders.ejs is
<h1> Display Orders Here </h1>
<% if(myorders.length>0) { %>
<table border="1">
<tr>
<th>Index</th>
<th>Name</th>
<th>size</th>
</tr>
<% for(var i=0; i<hurleys.length;i++) { %>
<tr>
<td><%= i %></td>
<td><%= myorders[i].name %></td>
<td><%= myorders[i].orderssize %></td>
</tr>
<% } %>
</table>
<% } else { %>
No Record Found
<% } %>
MyApplication schema is
const mySchema= new Schema({
name: {
type: String,
required: true,
},
ordersize: {
type: String,
required: true,
},
});
module.exports = mongoose.model('MyApplication', mySchema);
You have to pass the query filter as the first parameter of the find method.
This should do the trick, assuming the username is stored in the Schema as name.
app.get('/vieworders', require('connect-ensure-login').ensureLoggedIn(), function(req, res) {
Myapplication.find({name: req.user.username}, function(err, myorders) {
res.render('vieworders', { user: req.user, myorders: myorders});
console.log(myorders);
});
});

if statments are not working with ejs view engine

I'm trying to do "Orders page" that has a list of orders in a single page, but it should have seperate sections. One is for "Already made" and the other is for "Pending". It has a simple database with a field "isDone" that is either true or false. So as I imagine, if it is true, then it should appear on "Already made" section, but the problem is, that if statment doesn't work for some reason in .ejs.
<body class="container">
<main>
<div class="jumbotron">
<h1>Pending</h1>
</div>
<% orders_list.forEach(function(order){ %>
<% if(order.isDone == false) { %>
<div>
<h3><a href='<%= order.url %>'><%= order.name %></a> - <%= order.price %> euru</h3>
<hr>
</div>
<% } %>
<% }) %>
<div class="jumbotron">
<h1>Already made</h1>
</div>
<% orders_list.forEach(function(order){ %>
<% if(order.isDone == true) { %>
<div>
<h3><a href='<%= order.url %>'><%= order.name %></a> - <%= order.price %> euru</h3>
<hr>
</div>
<% } %>
<% }) %>
</main>
</body>
var express = require('express');
var router = express.Router();
var Order = require('../models/order');
router.get('/orders', function(req, res){
Order.find({})
.exec(function(err, list_orders){
if (err) {return next(err)};
// If Successful
res.render('../views/pages/index', {orders_list : list_orders});
});
});
module.exports = router;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var OrderSchema = new Schema(
{
isDone: {type: Boolean, required: true},
name: {type: String, required: true},
quantity: {type: Number, required: true},
price: {type: Number, required: true},
phone: {type: Number},
});
OrderSchema
.virtual('url')
.get(function(){
return '/order/' + this._id;
});
module.exports = mongoose.model ('Order', OrderSchema);
I expect 'isDone' == true orders to go to 'Already made' section, and 'isDone' == false orders should go to the 'Pending' section. But not even single item appears. Database is connected successfully, if there are no if statments then orders appear on the page.
In your if condition you have quote your true value because ejs file is taking it as a value instead of a boolean value , so quote your true like this and it will work fine.
<% if(orders_list[i].isDone == 'true'){ %>
<div>
<h3><a href='<%= orders_list[i].url %>'><%= orders_list[i].name %></a> - <%= orders_list[i].price %> euru</h3>
<hr>
</div>
<% } %>

how to access the object in ejs

IMAGEThis is how ist's displayed on the web
I want to access the object part to display the data using EJS. I have failed to reach the object data.
My Schema
let Subject = mongoose.Schema({
name:[],
crh:[]
});
let Program = mongoose.Schema({
name: String,
semester: Number,
subject:[Subject]
});
let School = mongoose.Schema({
name:{
type: String,
},
program:[Program]
})
POST METHOD TO ACCESS THE DATE FROM THE DB
router.post('/view',(req,res) =>{
let school = req.body.school;
let semester = req.body.semester;
let program = req.body.program;
All.find({name:school,"program.name":program,"program.semester":semester},(err,record) =>{
if(err) throw err;
if(!record){
req.flash("error","We couldn't find any relevant document! ");
res.redirect('/admin/view');
}
if(record){
console.log(record);
res.render("./admin/viewSub",{find:record});
}
})
})
Consoled data! How do i access the data of the object from ejs. I want to display the data inside the object in table. I just want to know how to access it, when i try to use "." to reach the points; i am unable to reach the data.
[ { program: [ [Object] ],
_id: 5a9045474b530750a4e93338,
name: 'sose',
__v: 0 } ]
Try this.
<ul>
<li><%= find.name %></li>
</ul>
<ul>
<%for (var prog in find.program){%> //to fetch data from array of array
<li><%= prog.name %></li>
<li><%= prog.semester %></li>
<ul>
<% for (var sub in prog.subject){%>
<li><%= sub %></li>
<%}%>
</ul>
<%}%>
</ul>

How do I update a form that has multiple values in ejs + mongoose?

a user has fields in mongoose which will get updated if the user decided to update.
Here's the user schema
var User = Schema({
education: [{ type: String}],
});
So basically a user has fields that they could update or add, for example a user can add additional education and skills information using a form.
How do I properly do it in ejs and route?
my attempt in the route.js
router.post('/update-resume', function(req, res) {
User.findById(req.user._id, function(err, foundUser) {
// This part how do I update ?
if (req.body.education) foundUser.resume.education.push(req.body.education);
foundUser.save();
});
});
The value keeps pushing, i want to , I know that it is obvious that I'm pushing the data to the field, but how do I update it properly?
Form.ejs
<div class="form-group">
<label for="education">Education:</label>
<% for(var i = 0; i < user.resume.education.length; i++) { %>
<input type="text" class="form-control" name="education" id="education" value="<%= user.resume.education[i] %>">
<% } %>
</div>
Is it true that I need to for loop each field? if I want to update the specific data?
1st option:
Depending on how you use the application, you might not even care about updating - you could just delete the previous educations and save new ones instead.
2nd option:
To properly update you really need some kind of an ID that you can refer to when updating, right?
You'll still need to use your for loop, you just need to insert the id hidden field.
To do that you will need to pass an object and not an only-string value. I have made my object array look like this:
var education = [
{content:'Education 1',id:1},
{content:'Education 2',id:3},
{content:'Education 3',id:5},
{content:'Education 4',id:2},
];
Then you can do something like that:
<% for(var i = 0; i < education.length; i++) { %>
<input type="hidden" type="hidden" name="education_id" value="<%= education[i].id %>"/>
<input type="text" class="form-control" name="education" id="education" value="<%= education[i].content %>">
<% } %>
Array will always get passed the way you have sent it to the server. In my case, I'll get this (you can see that everything's in the order that it should be):
{education_id: [ '1', '3', '5', '2' ],
education: [ 'Education 1', 'Education 2', 'Education 3', 'Education 4' ] }
Now, let's look at the POST backend, you will need to tie everything back to an object (you don't really need to, but you can and probably should for the sake of sanity):
var education_i;
var education_req = [];
for(education_i=0;education_i<req.body.education.length;education_i++) {
console.log(req.body.education[education_i]);
education_req.push({
content:req.body.education[education_i],
id:req.body.education_id[education_i]
});
}
A few more notes:
You HAVE TO check for the user of every education record. You don't want to let anyone mess with the IDs and ruin someone else's profile.
You should probably save the array length variable separately, outside of the loops as it might cause performance issues on very large arrays because the length is parsed on every loop iteration.
This is the way I would do it:
The model:
var User = new Schema({
education: [{ content: String }]
});
The EJS/HTML:
<form id="education">
<% user.education.forEach(function(item) { %>
<input type="text" data-id="<%= item.id %>" value="<%= item.content %>" />
<% }); %>
<button type="submit">Save</button>
</form>
The client side javascript (JQuery):
$('#education').on('submit', function(e) {
e.preventDefault();
var data = [];
$(this)
.find('input')
.each(function() {
var $this = $(this);
// Collect the data with the id and value
data.push({
id: $this.data('id'),
value: $this.val()
});
});
$.ajax({
url: '/update-resume',
type: 'post',
data: { data: JSON.stringify(data) }
})
.done(function(data) {
if (data.success) {
// Lazy: refresh window
window.location.reload();
}
})
.fail(function() {
// Show an error or something fancy
});
});
The above javascript will read the data-ids from the input and the values and put them into an array of education objects. It will then stringify the object add and the string to the key 'data'. This means that you can pull the string in the route from req.body.data and parse it.
The server side javascript/in the route:
router.post('/update-resume', function(req, res, next) {
User.findById(req.user._id, function(err, user) {
var parsed = JSON.parse(req.body.data);
// update and remove
var results = user
.education
.filter(function(item) {
return parsed.some(function(input) {
return input.id === item.id;
});
})
.map(function(item) {
var related = getRelated(item.id, parsed);
return { content: related.value };
});
// Add new items
user.education = results
.concat(parsed.reduce(function(prev, curr) {
if (!getRelated(curr.id, results)) {
prev.push({ content: curr.value });
}
return prev;
}, []));
user.save(function(err) {
if (err) return next(err);
res.json({ success: true });
});
});
});
Get related helper:
var getRelated = function(id, arr) {
for (var i = 0; i < arr.length; i++) {
if (String(arr[i].id) === String(id)) return arr[i];
}
};
Mongoose will automatically give your education array items an id. The above will allow you to be able to add, remove and update existing education items on the page.

Resources