How to label HTML for saving nested objects in Mongoose? - node.js

I have a model that looks like this:
const recipeSchema = new Schema({
title: { type: String , required: true},
description: { type: String , required: true},
steps:[{
text:{type:String},
ingredients:{type:String}
}]});
Using a bodyparser, I'm able to save data by simply tagging them in the name attribute of the HTML form. Like below:
<div class="input-field">
<textarea id="title" name="title" placeholder="Enter title here"></textarea>
<label for="title">Title</label>
</div>
This method works well for the first 2 fields (title and description), but I'm stuck on how to label them for the steps field. How would I make the code understand which input fields are for step.text and which are for steps.ingredient? And create an array of objects?

Figured it out. For anybody else who may stumble across this post, here's how you do it.
Basically you need to refer to the object in the array as (per the example above) in the following format:
step[0][text]
I couldn't find it anywhere in the documentation, but finally got it from this link:
http://www.thiscodeworks.com/how-to-save-input-from-html-form-to-json-file-using-body-parser-html-nodejs/5c44c4722178800014d5f127

How are you passing your data to the html page? I think using a view engine like EJS solves your problem better. It allows you to pass your data to a view, and then inject the data directly into your html elements.

Related

Can't render array elements from mongodb document with express-edge templating

I'm following this blog tutorial to learn nodejs backend along with mongodb, it seems a bit outdated(I've had to tweak some stuff to make it work) but also I'm not following it 100%, as I'm making my own front end instead of using a theme and I'm using my own database, which brings to the problem:
While rendering the post lists I want to render inside each post the list of it's tags, which in my database is an array of strings, but it doesnt work. When I try to access the first element of the array only, it return undefined.
This code doesnt render any <li>:
<div class="row" id="lista-posts">
#each(post in posts)
<div class="col-12">
<h4>{{post.titulo}}</h4>
<ul>
#each(tag in post.tags)
<li>{{tag}}</li>
#endeach
</ul>
<div class="post-conteudo">
{{post.conteudo}}
</div>
</div>
#endeach
</div>
This one here render one <li> (as expected) but it's written Undefined:
(...)
<h4>{{post.titulo}}</h4>
<ul>
<li>{{post.tags[0]}}</li>
</ul>
All the other elements like "titulo" and "conteudo" are rendered fine. For context, every post in my db has:
_id: IdObject
titulo: String
tags: Array of Strings
conteudo: String
Turns out it's because I didn't set up the tags array in my mongoose Schema.

Cast to ObjectId failed for value "req.params.id" at path "_id" for model "Project"

I am trying to create a clone of employee management system. I want to implement a feature that when admin opens a details page for any project he will get list of available employees as checkboxes and when select them and submit then those employees will be added to the given project.
I am developing it using Node.js and MongoDB
My project schema is -
const ProjectSchema = new mongoose.Schema({
name: String,
deadline: Date,
description: String,
manager:{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
},
employees: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
],
created_on: {
type: Date,
default: Date.now
}
});
Form -
<form action="/admin/project/<%= project._id %>/addmembers" method="POST">
<div class="row">
<% employees.forEach((employee) => { %>
<% if(!employee.projects.includes(project._id)){ %>
<div class="col-12 col-sm-6 col-md-4 col-xl-3">
<input type="checkbox" value="<%= employee._id %>" name="newMembers"> <%= employee.name %> </input>
</div>
<% } %>
<% }); %>
</div>
<hr class="my-2">
<p class="lead">
<button class="btn btn-blue btn-lg">Add Members</button>
</p>
</form>
Code for handling the request -
router.post("/project/:id/addmembers", (req, res) => {
console.log("add member post route");
Project.findById(req.params.id, (err, foundProject) => {
if(err){
console.log(err);
req.flash("error", err.message);
res.redirect("back");
} else {
req.body.newMembers.forEach((employee) => {
foundProject.employees.push(employee);
})
foundProject.save()
console.log(foundProject);
req.flash("success", "Successfully added new members");
res.redirect("/admin/project/req.params.id");
}
})
});
Now whenever I submit form users are added but I always get an error Cast to ObjectId failed for value "req.params.id" at path "_id" for model "Project" instead of success message now I am new to mongo so I googled it but can't solve my problem can anyone explain me why this error is coming and how can I fix it?
Also I know that if if only one checkbox is selected then req.body.newMembers will not be array. If you can you provide better method to do it, it will be very helpful?
Update -
I also tried findOne(), find(), findByIdAndUpdate(), and id = mongoose.Types.ObjectId(req.params.id) but still get the same error message
It looks like what is happening is you aren't properly handling ids and _id. Given that you haven't mentioned anything, I assume you see this error only once. However, given that the error has to do with the project model/schema, and you use almost the same code for both manager and employees, the error should have to do with a difference between the two. The main difference I see is manager has id: {*id stuff*}, but employee has {*id stuff*} with no id:. However, I don't think this is the actual error, but just something to keep in mind. Try also changing the id type to something like number just to see if this is related to the issue.
What I think that actual error is is the fact that you use req.params.id in a findById(). This searches your database using the _id. First, having id and _id both is a bit redundant (if you have a reason, that's fine, but _id is something MongoDB needs, so it might be better to just stick with it as it is precreated and handled). Second, you are using a string/int as an id search. To test whether this is the issue, try changing findById() to something like findOne() or find(). Just to clarify, findById() doesn't explicitly need an ObjectId, so the type may not be the issue, but findById() does handle the search differently than find({_id: id}).
Another thing to check is that req.params.id actually gives the right value. It should, but this is something to verify. To do so, just console.log() it. If this is the problem, try moving :id to the end of the path.
In response to your edit:
Make sure that the problem doesn't have to do with not having id: in the employee (as stated above) and that it doesn't have to do with the id being a ObjectId rather than a number. If you change the id to a number, make sure to change the search so that it finds the object by id not _id. Just to let you know, every MongoDB object has an _id (even subobjects like employee and manager), so using id is somewhat redundant.
To give you more things to try: make sure you console.log() req.params.id to make sure it has the right value. Then, also try commenting sections of code until you find which line it is on. Try hardcoding some input until it works.

I have an array that is generated on page, saved as an variable. How to to send it with a POST to db (mongodb, node, express)

A node, express, mongodb question.
I have a webpage, with a some JS-code. The user types some things into the my form and then they can generate a table with data. Before the table is printed to the user the data as saved as a variable (Array). The array itself is named obj.invoices.
I can easily save the data from the form to the DB using the "name". When I use it shows up in the req.body, which i then can use in my controller and save it to the DB.
But how do i pass the generated variable (obj.invoices) from the page so that it will follow along in the POST and shows up in the req.body?
As for now the array isn't parsed so i cant build a function in express/mongoose to save the data to the DB.
I solved it but I'm quite sure this is a bad solution.
When finished generating the array I run this function,
function showArray() {
var json_data = JSON.stringify(myArray);
document.getElementById('showArray').innerHTML = json_data;
}
This convert the array to string, and then post it into a input within my form;
<div class="field">
<div class="control">
<textarea id="showArray" class="textarea is-info" type="text" name="ArrayToDb"></textarea>
</div>
</div>
So when i submit my form the array as a string is posted with the req.body. Then in my controller.js for the app I convert the string back to an array;
let jsonArray = JSON.parse(req.body.ArrayToDb)
and then i save it to the DB
newLan.fakturor = jsonArray;
newLan.save(function (err) {
console.log(newLan._id)
});
Like I said, this is most likely a really bad way to do it but it works for me, for now.

How do I update a specific document from a form through mongoose?

Struggling with this problem because I'm missing some fundamentals I think, but after hours looking through the docs I think I need some help :)
I have a (simplified) collection as so:
const ExampleSchema= new Schema ({
Word: String,
Difficulty: String,
)};
Which I am succesfully displaying on a page as so:
<% ExampleSchemas.forEach(exampleschema=> { %>
<p><%= exampleschema.word %></p>
<p style="display: none" name="ExampleSchemaID" value="<%= ExampleSchema._id %>"</p>
<% }) %>
For each word I have a form below, I would like the user to be able to select easy, ok or hard and for this to set the value in the DB.
<form method="PUT">
<button formaction="/review/feedback/easy">Easy</button>
<button formaction="/review/feedback/ok">Ok</button>
<button formaction="/review/feedback/hard">Hard</button>
/form>
I have played around with a route like this to no avail
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate(
req.params.ExampleSchema._id,
req.Difficulty= easy);
The further issue is that I would like to display x number of these documents to the user, therefore I need a route that gets only that specific word.
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate({"_id": req.params.ExampleSchema._id},{"Difficulty": "3"});
but please not your difficulty is a number, so why not use a number instead:
const ExampleSchema= new Schema ({
Word: String,
Difficulty: Number,
)};
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate({"_id": req.params.ExampleSchema._id},{"Difficulty": 3});

Create a Schema object in Mongoose/Handlebars with custom keys/values

I want to create a form to input custom keys and values of an object in an mongo/mongoose schema to eventually see in a handlebars view. See example to better explain. Any help would be great. :)
Mongoose/Mongodb Schema:
var docketSchema = new Schema({
staff: [{ String: String, String: String }]
});
Handlebars input view:
<div class="form-group">
<input value="{{input.staffkey1}}">
<input value="{{input.staffvalue1}}">
</div>
<div class="form-group">
<input value="{{input.staffkey2}}">
<input value="{{input.staffvalue2}}">
</div>
the reason to use mongoose is usually to ensure that your documents have some known keys and to validate new objects so they conform to your schema.
If you explicitly don't want your objects to have the same keys use the schema-type Mixed - http://mongoosejs.com/docs/schematypes.html:
var docketSchema = new Schema({
staff: [{}]
});
You can add strict: false to your schema to add fields to your schema which are not defined.
var docketSchema = new Schema({
//
}, {strict: false});
Nevertheless it is always better to define your fields.

Resources