I want to access the data from my local mongoDB and I want to search for all keys without making a state{} wherein I will have to pre-define what kind of keys are present in the database.
Now although this problem has been answered numerous times by different people , most of them have the data in the following format ( it is a random example):
{
"object": {
"name": "Pluralsight",
"number": 1,
"address": "India",
"website": "https://www.pluralsight.com/"
}
}
or like this:
{
"String":"EternalSunsetOfCloudedMind",
"Rating": 2
}
In the above two examples we can easily use the function : <p>Name: {sampleJSON.object.name}</p> or <p>String : {sampleJSON.string}</p>
But what if the database looked like this:
[{"_id":"60d1b5493b470b3884325872","Title":"CatManBegins" ,"Image":"https://m.media-amazon.com/images/catmanbegins.jpg","Rating":9},
{"_id":"60d1b5d0b25e04287318a072", "Title":"Cabdriver","Image":"https://m.media-amazon.com/images/cabdriver.jpg"},
{"_id":"60d314a981ecb624dc6966a2","Title":"Pulpnonfiction" ,"Image":"https://m.media-amazon.com/images/pulpnonfiction.jpg","Review":{"AlphaReview":"WOW!","BetaReview":"Okay nice","GamaReview":"Hmm"}},
{"_id":"60d32406affa146b4b1428a2", "Title":"DoctorNormal","Category":"Marvellous Movies"},
{"_id":"60d5cfc6326f1c336d3478e2", "Title":"GameOfKingdoms","BudgetInDollars":100000}]
How can I do the same in this kind of database?
My current knowledge and progress:
Till now I have been able to fetch the data from MongoDB to the server-side, from which I have been able to send the JSON to the client-side after parsing it using the following code:
Server.js
app.get('/run-query',async(req,res)=>{
MongoClient.connect('mongodb://localhost:27017',{useNewUrlParser: true, useUnifiedTopology: true}, function(err,db){
if(err) throw err;
var dbo = db.db("MyDatabaseName");
dbo.collection("MyCollectionName").find({}).toArray(function(err,result){
if(err) throw err;
res.json(result);
db.close();
});
});
})
Client/src/App.js
function App() {
const [data, setData]= useState(null);
useEffect(()=>{
fetch('/run-query')
.then ((res)=>res.json())
.then (setData)
.catch(console.error)
}, []);
return (
<div>
<h1>All results </h1>
<div class="box">
<p>{!data ? "loading..." : JSON.stringify(data)}</p>
</div>
</div>
);
}
When you want to display an array, usually you’ll want to map each item in the array to a React element.
To get all property names and values of a JavaScript object, you can use the Object.entries() function.
Putting these two together:
// maps each object in the array to an unordered list of key/value pairs
const dataItemToKeyValues = (item) => {
const entries = Object.entries(item);
const listItems = entries.map(([key, value]) => (
<li>
{key}: {value}
</li>
));
return <ul>{listItems}</ul>;
};
return (
<div>
{!data ? (
"Loading"
) : (
<ul>
{data.map((item) => (
<li>{dataItemToKeyValues(item)}</li>
))}
</ul>
)}
</div>
);
The React documentation has a section on lists that has more information.
Related
There is another post with the same problem with me ,and the solution didnt work for me . So i have code like this :
app.post("/updatestatus", (req, res) => {
let id = req.body.id;
let status = req.bo.status;
imgModel.updateMany(
{ _id: { $in: id } },
{ $set: { status: status } },
(err, items) => {
if (err) {
console.log(err);
res.status(500).send("An error occurred", err);
} else {
res.json({
code: "200",
message: "update successfully",
data: items,
});
}
}
);
I want to update the status of the inputed id . I also want to update many id at one time, but the code only take 1 id at one time .if i request like this
{ "id":"1",
"id:"2"}
The id will be duplicated and updated only the one below. Im thinking of using a for loop like this :
for ( let i =0, i<= 100, i++){
let id[i] = req.body.id;
}
But it will error because i declared the varible wrong way!How can i fix this?
A Javascript object like req.body cannot contain the same key twice. If you want multiple values for the key id, then express.urlencoded({extended: false}) will translate the request payload id=1&id=2 into
req.body = {"id": ["1","2"]}
and you can work with the array req.body.id.
A request with that payload can be produced with
<form action="/updatestatus" method="post">
<input name="id"/>
<input name="id"/>
<input type="submit"/>
</form>
but then the maximal number of ids that can be input is fixed (2 in this example).
I want to fetch data from API and show frontend using react but I am getting error from frontend side which is (TypeError: answers.map is not a function ) so how can I solve this error --
MY CODE IS -
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
import { getPolls } from "../helper/coreapicalls";
const MainPoll = () => {
const [polls, setPoll] = useState([]);
const [error, seterror] = useState(false);
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([]);
useEffect(() => {
loadPoll();
}, []);
const loadPoll = () => {
getPolls().then((data) => {
if (data.error) {
seterror(data.error);
} else {
setPoll(data);
console.log(data);
}
});
};
// Handling user vote
// Increments the votes count of answer when the user votes
const handalchange = () => {
console.log("hello");
};
return (
<div className="">
<div className="container">
<h1 className="blog_heading">Poll's of the Day</h1>
<div className="row">
{polls.map((poll, index) => (
<div className="col-lg-4 col-12" key={index}>
<Poll
question={poll.question}
answers={poll.options.none}
onVote={handalchange}
/>
</div>
))}
</div>
</div>
</div>
);
};
export default MainPoll;
Data which I am getting from API is-
Here I have Question , 3 options how can I show to frontend
Error -
There is two little mistakes in the code that you show us:
the first One you imported import Polls from "./polls"; and you call <Poll noStorage question={poll.question} answers={poll.options} onVote={handleVote}/> just change Poll by Polls.
const [pollAnswers, setPollAnswers] = useState([...answers]); this didn't work because you need to pass a initial value for your state and answer is not yet initialize and accessible. just change useState([...answers]); by useState([]);
UPDATE:
you need to pass an array to answers props .
We can see in your console screenshot that the array of options has "none" as key so
try this : <Poll noStorage question={poll.question} answers={poll.options.none} onVote={handleVote}/> ("none" is a strange key...)
UPDATE
Your data object is not well formated to fit react-polls answers props.
in the npmjs doc of react-polls we can see an example of options and it's an array of object like this:
[
{ option: 'Yes', votes: 8 },
{ option: 'No', votes: 2 }
]
so based on the data console log that you add in your question it should looks like this:
[
{
createdAt: "2020-12-01T21:43:23:21.061Z",
options: {
none: [ { option: 'Yes', votes: 8 },
{ option: 'No', votes: 2 }],
student: ["12345678978945"],
teacher: ["7894567894231"]
},
question: "Are you student ot teacher",
updatedAt: "2020-12-01T21:43:23:21.061Z"
}
]
see a sandBox here working with your code (except getPolls()).
I think the issue come from the API.
Recently, I picked up Vue.js and Mongoose to develop a personal project to mainly track Ingredients' on hand quantities on a certain online game.
Different dishes require different ingredients. Lotus Seed - Bird Egg soup, Jewelry Soup and Jade Parcels all require different number of Lotus Head as its ingredients.
I update an ingredient's quantity by using updateOne on ingredients collection.
Unfortunately, I originally embedded the ingredients on foods / dishes, which I realized problematic
recently, coz literally you just count ingredients what you currently have.
So a food document now looks like this
{
"_id" : ObjectId("5fca4ada32195d5814510242"),
"foodName" : "Lotus Seed and Bird Egg Soup",
"onHandQty" : 20,
"ingredients" : [
"5fca481432195d581451023f",
"5fca483932195d5814510240",
"5fca48a232195d5814510241"
]
}
I read about Mongoose's populate(), and tested to output one food/dish. Unfortunately there's nothing coming out of Vue.js front-end after trying that code.
server/models/Food.js
const { Router } = require('express');
const FoodItem = require('../../models/Food');
const IngredientItem = require('../../models/Ingredient');
const router = Router()
router.get('/', async(req, res) =>{
try {
const food = await FoodItem.findOne({
foodName: 'Lotus Seed and Bird Egg Soup'
}).populate('ingredients').
exec(function (err, food) {
if (err) return handleError(err);
console.log('The food is %s', food.foodName);
});
res.send(food);
} catch (error) {
res.status(500).json({
message: error.message
})
}
});
module.exports = router
A portion of component where ingredients are rendered
client/src/components/Food.vue
<div class="tile is-ancestor">
<div class="tile">
<div class="tile is-parent">
<div class="tile is-child box">
<template v-if="food.ingredients">
<div class="ingredients-block">
<p>Ingredients List:</p>
<ul class="ingredients-list">
<li class="row" v-for="ingredient in food.ingredients" :key="ingredient._id">
<div id="ingredient-image-container">
<img class="image is-64x64" :src="require(`../assets/images/food_inv/${ingredient.imagePath}.png`)" alt="ingredient.ingredientName" :title="ingredient._id">
{{ingredient.ingredientName}}
</div>
<div class="required-qty-container">
<!-- <i class="material-icons" id="required-inner-qty">food_bank</i> -->
Required:
{{ ingredient.requiredQty }}
</div>
<div class="on-hand-qty-container">
<p>On Hand:</p>
<input v-if="ingredient.onHandQty < ingredient.requiredQty" class="input is-danger on-hand-input" type="number" v-model="ingredient.onHandQty" min="0">
<input v-else class="input is-primary on-hand-input" type="number" v-model="ingredient.onHandQty" min="0">
<!-- <button class="button is-primary save-button" #click="test({ingredient_id: ingredient._id, onhandqty: ingredient.onHandQty})"><i class="material-icons">save</i></button> -->
<button class="button is-primary save-button" #click="$emit('update-qtys', {ingredient_id: ingredient._id, onhandqty: ingredient.onHandQty})"><i class="material-icons">save</i></button>
</div>
</li>
</ul>
</div>
</template>
</div>
</div>
</div>
</div>
Whole project on Github: Food Inventory
Quick Fixes,
change your food schema's ingredients field from object to array,
const foodSchema = new mongoose.Schema(
{
foodName: String,
imagePath: String,
effect: String,
onHandQty: Number,
// correct this to array
ingredients: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Ingredient'
}]
}
);
there are 2 ways to call mongoose methods first exec() with callback and second without exec() callback,
exec with callback that you have used buy need to send response (res.send(food) or res.json(food)) from inside the exec call function,
router.get('/', async(req, res) =>{
try {
await FoodItem.find()
.populate('ingredients')
.exec(function (err, food) {
if (err) return handleError(err);
console.log('The food is %s', food);
// put response here
res.json(food);
});
} catch (error) {
res.status(500).json({ message: error.message })
}
});
exec without call back
router.get('/', async(req, res) =>{
try {
const food = await FoodItem.find()
.populate('ingredients')
.exec();
res.json(food);
} catch (error) {
res.status(500).json({ message: error.message })
}
});
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});
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.