This is the form I am using :
<form method="post" enctype="multipart/form-data">
<label class="label">Class Name</label>
<label class="input">
<input type="text" name="class_name[]">
</label>
<label class="label">Grade</label>
<label class="input">
<input type="text" name="grade[]">
</label>
</form>
Below is the schema I am using :
var SchoolSchema = new mongoose.Schema({
classroom: [{
Name: {
type: String,
required: true
},
grade: {
type: Number,
default: 1
}
}]
});
Firstly I tried saving the data using below function :
var School = new mongoose.Schema.model('School');
exports.submit = function(req, res) {
var classRooms = [];
for (var i = 0; i < req.body.class_name.length; i++) {
classRooms.push({Name: req.body.class_name[i], grade: req.body.grade[i]});
}
School.create({classroom: classRooms}, function(err, school) {
...
});
};
It worked fine when there was only a single entry in the form But for multiple entries it enters all the entries in single document. For e.g if through form I enter
Name: ABC Grade:1
Name: EFG Grade:2
This data dets saved in data base as
classroom:[{Name:ABC,EFG, Grade:1,2}]
and it should be like
classroom:[{Name:ABC, Grade:1},{Name: EFG, Grade:2}]
I even try to change the function as below:
var School = new mongoose.Schema.model('School');
exports.submit = function(req, res) {
var classRooms = [];
for (var i = 0; i < req.body.class_name[0].length; i++) {
classRooms.push({Name: req.body.class_name[0][i], grade: req.body.grade[0][i]});
}
School.create({classroom: classRooms}, function(err, school) {
...
});
};
Now this worked fine for multiple entries, but when I enter a single entrie it didn't worked as expected. For e.g. If I enter following data through form,
Name: AB Grade: 2
It gets saved in the database as
classroom:[{Name:A,Grade:2 },{Name:B,Grade:1}]
Would anyone please let me know what I am doing wrong here and how to correct this. Thanks !!
Related
I can retrieve the api data and console.log it and the for loop is displaying each card div element correctly but I cannot get the details to display for each item.
plant.js controller:
exports.postFindPlant = async (req, res) => {
const plantName = req.body.searchPlant;
const key = process.env.API_KEY;
const urlAPI = `https://trefle.io/api/v1/plants/search?token=${key}&q=${plantName}`;
try {
const response = await fetch(urlAPI);
const data = await response.json();
if (data.meta.total === 0) {
console.log('nada');
} else {
console.log(data.meta.total);
let plants = JSON.stringify(data);
res.render('plants', {
plants: plants,
pageTitle: 'Plants',
path: '/plants',
hasPlants: plants.length > 0
});
}
} catch (err) {
console.log(err);
}
};
plants.ejs:
<% for(var i=0; i < plants.length; i++) { %>
<div class="col s12 m4">
<div class="card">
<div class="card-image waves-effect waves-block waves-light">
<img class="activator" src="<%= plants[i].image_url %>">
</div>
<div class="card-content">
<span class="card-title activator grey-text text-darken-4"><%= plants[i].common_name %><i class="material-icons right">MORE</i></span>
</div>
<div class="card-reveal">
<span class="card-title grey-text text-darken-4"><%= plants[i].common_name %><i class="material-icons right">CLOSE</i></span>
<p><%= plants[i].scientific_name %></p>
<p>Plant Details</p>
</div>
</div>
</div>
<% } %>
In your Node.js code, you get the data from API with:
const data = await response.json();
Usually, some data returned from an API contains much information with keys, so it could be an Object. (I guessed, but confirmed in comments)
And then, if you want to access any info from an Object, you have to refer the value by it's key. For example:
> const person = { name: "Alan", age: 20 }
undefined
> person.name
'Alan'
> person.age
20
But if it's a object which contains an Array, and you want to access the Array's data, you can use Array index after refer the Array with it's key in the Object.
> const person = { name: "Alan", age: 20 }
undefined
> const person2 = { name: "Alice", age: 18 }
undefined
> const people = { type: "Human", data: [person, person2] }
undefined
> people
{
type: 'Human',
data: [ { name: 'Alan', age: 20 }, { name: 'Alice', age: 18 } ]
}
>
For example, how to get Alice in my code above?
> people.data
[ { name: 'Alan', age: 20 }, { name: 'Alice', age: 18 } ]
> people.data[1]
{ name: 'Alice', age: 18 }
> people.data[1].name
'Alice'
>
In your case, you parse the Object to JSON, and passed it into your Ejs code. It's still an Object, so I assume you know how to refer the values you want with correct keys in plants.ejs, and the things you need is only plants.data. Then, just change this line of your plant.js controller below:
let plants = JSON.stringify(data);
To something like:
let plants = JSON.stringify(data).data;
What makes the different? Take a look again to my examples above, this refer to people.data, which is an Array of Objects, instead of an Object which contains an Array of Objects.
So hope you have fully understand what is the problem, and how to solve it.
I have a very simple Mongo set up as shown below. This works perfectly to get data from an input field and save the data. All working.
My Question: How would I go about looping through the jobs variable on the front end and set the data up so that it would work with my model. Somehow I would need to get it into inputs so I can req.body?
Data:
var jobs = [
{
name: "Accountant"
salary: 0,
}, {
name: "Actor"
salary: 0,
}, {
name: "Actuary"
salary: 0
}]... + hundreds more...
My Mongo Schema:
var JobSchema = new mongoose.Schema({
name: String,
salary: Number
});
module.exports = mongoose.model('jobs' , jobSchema)
My post route:
router.post('/register', function(req, res) {
var job = ({
name: req.body.name,
salary: req.body.salary,
})
Form to post:
<form action="/register" method="post">
<textarea class='jobnames' type="text" name="name" placeholder="name"> </textarea>
<textarea class='2' type="number" name="salary" placeholder="salary"> </textarea>
<button >Submit</button>
</form>
You can try it using the insertMany() query .
req.body = [
{
name: "Accountant",
salary: 0,
},
{
name: "Actor",
salary: 0,
},
{
name: "Actuary",
salary: 0
}
]
db.collection.insertMany(req.body);
With the help of this query , you can insert multiple documents at a time .
unique id i.e., _id will be automatically generated .
For more information about insertMany() visit the official document
You can just create it with the array itself, i.e. pass the array jobs in the post request. For example:
// set up the model using the schema
var Job = mongoose.model('Job', JobSchema);
Job.create(req.body)
.then(records => console.log('created records', JSON.stringify(records, null, 2))
see Model.create
I have tried almost all the solutions but couldn't get this fixed.
I'm trying to get multiple collection of data (Mongodb Atlas) onto one ejs file. But I can't get data onto the rendered ejs sheet. But when I tested with locus I can see all the data received from the database up to res.render("/vendor/show"). But it is not passing onto ejs template.
I have three sets of data sitting on the mongo Atlas:
const vendorSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
});
const newpromoSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
vendor:String,
description:String,
sdate:String,
edate:String,
});
const neweventSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
description:String,
vendor:String,
sdate:String,
edate:String,
});
router.get("/vendors/:id",function(req,res) {
var events={}; //Create Empty event Object
var promotions={}; //Create Empty promotion Object
var vendors={};//Create Empty vendor Objext
Promotion.find({},function (err,allPromotions) {
if (err) {
console.log(err);
} else {
//Find Collection And Assign It To Object
promotions=allPromotions;
}
});
Event.find({},function(err, allEvents) {
if (err) {
console.log(err);
} else {
events=allEvents;
}
});
Vendor.find({},function(err,allVendors){
if(err){
console.log(err);
}else{
vendors=allVendors;
//find order collection and passing it to ejs templates
res.render("vendors/show",{ event:events, promotion:promotions vendor:vendors});
}
});
});
Show.ejs code as
<%=vendor.name%>
<%=promotion.name%>
<%=event.name%>
You need to handle the async process. In your current code, there are three async processes. Please go through following the link you will definitely get an idea about it.
https://blog.risingstack.com/mastering-async-await-in-nodejs/
if you need more help Don’t hesitate to comment.
I managed to get all data using the below function. But now I have another issue, need to filter all the Promotions and Events by vendor But it doesn't filter.
Data Schema
const neweventSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
description:String,
vendor:String,
sdate:String,
edate:String,
});
const Event = mongoose.model("Event",neweventSchema);
module.exports = Event;
//SCHEMA SETUP===============================================================================================================
const newpromoSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
vendor:String,
description:String,
sdate:String,
edate:String,
});
//compile into a model
const Promotion = mongoose.model('Promotion',newpromoSchema);
module.exports = Promotion;
//SCHEMA SETUP===============================================================================================================
const vendorSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
});
const Vendor = mongoose.model("Vendor", vendorSchema);
module.exports = Vendor;
Routes as below
router.get("/vendors/:id", function (req, res) {
Vendor.find({}, function (err, allVendors) {
if (err) {
console.log(err);
} else {
// //Find Collection And Assign It To Object
Event.find({}, function (err, allEvents) {
if (err) {
console.log(err);
} else {
Promotion.find({}, function (err, allPromotions) {
if (err) {
console.log(err);
} else {
//find order collection and passing it to ejs templates
res.render("vendors/show", {event: allEvents, promo: allPromotions, vendor: allVendors});
}
});
}
});
}
});
});
EJS Show page But It doesn't get filtered.
<!-- Trying to filter all the events by vendor if event's vendor name == vendor name then show on the show page -->
<%if(event.vendor === vendor.name){%>
<div class = "container-fluid">
<div class = "row">
<%event.forEach(function(events){%>
<div class="col-sm-6 col col-md-4 col-lg-3">
<div class="thumbnail">
<img src ="</%=events.image%>" class="rounded" width="304" height="236">
<div class="caption">
<h4><%=events.name%></h4>
<p class="font-italic font-weight-bold text-muted">
<%=events.vendor%>
</p>
<p><strong>Start Date</strong><%=events.sdate%></p>
<p><strong>End Date</strong><%=events.edate%></p>
</div>
<p>Event Details <br>
<%=events.description.substring(0,100)%>
</p>
<p>
Find More
</p>
</div>
</div>
<%})%>
</div>
</div>
<%}%>
<!-- //Trying to filter all the promotions by vendor if vendor name= to promotions's vendor name only show in the show page-->
<%if(promo.vendor===vendor.name){%>
<div class = "container-fluid">
<div class = "row">
<%promo.forEach(function(promotions){%>
<div class="col-sm-6 col col-md-4 col-lg-3">
<div class="thumbnail">
<img src ="<%=promotions.image%>" class="rounded" width="304" height="236">
<div class="caption">
<h4><%=promotions.name%></h4>
<p class="font-italic font-weight-bold text-muted"> <%=promotions.vendor%></p>
<p><strong>Start Date</strong> <%=promotions.sdate%></p>
<p><strong>End Date</strong> <%=promotions.edate%></p>
</div>
<p>
<%=promotions.description.substring(0,100)%>
</p>
<p>
Find More
</p>
</div>
</div>
<%})%>
</div>
</div>
<%}%>
Good day everyone, I am building a web application to store employee information in a local database. I have written the necessary code so that the client can input the required information such as Name, email, phone number etc. via a form and save it to MongoDB. Now I would like to add a file upload feature so that documents can be uploaded alongside employee information, such as insurance policies, passport copies etc. I am struggling to find a way to implement this as a lot methods online show implementations of CRUD and file uploads separately. Is there anyone experienced who would be able to suggest an implementation approach? I'm new to Node dev and not familiar with the vast selection of available frameworks. So far I have the following set up:
Frameworks used: Express, Mongoose, Express-Handlebars (view engine), Bootstrap
employee.Model:
const mongoose = require('mongoose');
const mongoURI = 'mongodb://localhost:27017/testDB'
const conn = mongoose.createConnection(mongoURI, {useNewUrlParser: true});
Schema = mongoose.Schema;
var employeeSchema = new Schema({
fullName: {
type: String,
required: true
},
email: {
type: String
},
mobile: {
type: String
},
city: {
type: String
}
});
const Employee = conn.model('Employee', employeeSchema);
module.exports = Employee;
Employee Controller:
const mongoose = require ('mongoose');
const express = require ('express');
var router = express.Router();
const Employee = require('../models/employee.model');
router.post('/', (req, res) => {
insertRecord(req, res);
});
function insertRecord (req, res) {
var employee = new Employee();
employee.fullName = req.body.fullName;
employee.email = req.body.email;
employee.mobile = req.body.mobile;
employee.city = req.body.city;
employee.save((err, doc) =>{
if (!err)
res.redirect('employee/list');
else {
console.log('Error during record insertion: ' + err);
}
});
}
Handlebars View:
<h3>{{viewTitle}}</h3>
<form action="/employee" method="POST" autocomplete="off">
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName"
placeholder="Full Name">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email"
placeholder="Email">
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile"
placeholder="Mobile">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city"
placeholder="City">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-
database"></i> Submit</button>
</div>
</form>
Uploaded files are inputs just like others, they just need the specific enctype="multipart/form-data" attribute on the HTML side, and a proper handling on the server.
And easy-to-grasp demonstration using Multer (based on their own examples actually):
const upload = require('multer')({dest: 'uploads/'}); // npm i multer
const fileUploadMiddleware = upload.fields({
{name: 'resume', maxCount: 1},
{name: 'insurance', maxCount: 4}
});
router.post('/', fileUploadMiddleware, (req, res) => {
insertRecord(req, res);
});
function insertRecord(req, res) {
...
console.log(req.files.resume[0].mimetype); // this is resume file type
console.log(req.files.insurance[0].path); // this is first insurance file path
console.log(req.body.fullName); // this still works
}
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.