Cannot display api data in ejs template - node.js

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.

Related

Cant Map() Through An Array Of Objects

Been trying to solve this one for quite a while now.
I am saving an array which contains objects to my data base,
When I try to map() through it to retrieve the object's properties its just rendering nothing.
This is the app.js code :
app.get("/:plasticcategory/product/:plasticproduct", (req, res) => {
const plasticCategory = _.startCase(_.toLower(req.params.plasticcategory));
const plasticProduct = req.params.plasticproduct;
Product.find({category: plasticCategory, title: plasticProduct},'alt', (err, foundItem) => {
if(err){
console.log(err)
}else {
console.log(foundItem);
res.render('alternatives', {altProduct: foundItem});
}
});
});
When I console.log(foundItem) the result is [ { _id: 5f5f9b2a9f999b1e9009072b, alt: [ [Object] ] } ]
This is my ejs code(trying to render the alt's array objects properties:
<% altProduct.map(alt => {%>
<div class="col-lg-3">
<h1><%=alt.altTitle %></h1>
<img src="<%=alt.altImage%>" alt="alt-image">
Get it now!
</div>
<% }) %>
I have added images to make it more clear, Thank You <3
enter image description here
When you render the template, I see you call it like this:
res.render('alternatives', {altProduct: foundItem});
Where foundItem is the array [{ id: 'something', alt: [{ someObject }] }].
This is an array of results. Each of those results has a key called 'alt' with items in it. If you want to render all of those items together, you will need to compile them all into a single array. (This is called 'flat-mapping'.)
Starting inside the else block of the callback of Product.find:
const itemArrays = foundItem.map(item => item.alt); // Get the inner array from each result
const allAlternativeProducts = [].concat(...itemArrays); // Collect all these products into a single array
res.render('alternatives', {altProduct: allAlternativeProducts});

How to pass data from Node.js to embedded Javascript in EJS Template?

Trying to pass data fetched from our SQL server to an EJS template with some embedded Javascript. Here are some snippets of my code to show you what I am trying to do.
Route file
const data = await sqlCalls.getMenuAccess(req.user.UserIdNo)
const data2 = await sqlCalls.getAvailability();
const menus = data.recordset
const availability = data2.recordset
res.render('portal-availability',
{
title: 'Availability',
name: req.user.FirstName,
menudata: menus,
availabilitydata: availability // <--- This is the data that I want to use in my EJS template.
});
});
HTML Portion of my EJS template
<section class="content">
<div class="container-fluid">
<div class="block-header">
<h2>AVAILABILITY</h2>
</div>
<div id="grid"></div>
</div>
</section>
Javascript in my EJS template
<script>
$(document).ready(function() {
$("#grid").kendoGrid({
dataSource: {
data: availability, // <---- This is where I am trying to use availability to bind to my grid.
schema: {
model: {
fields: {
ItemIdNo: { type: "number" },
cultivardescription: { type: "string" },
containerdescription: { type: "string" },
week0: { type: "number" }
}
}
}
});
});
</script>
Any help would be greatly appreciated.
Thanks!
Jim

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.

How to save form input array in mongodb?

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 !!

Resources