Model is created but can't update with Express, Mongoose, and NodeJS - node.js

Alright so I'm having a problem with updating a model. I can create a Document and it works just fine but when I try to update, I get an error.
/Users/User/Sites/project/app.js:182
a.features.kids = req.body.a.features.kids;
^
TypeError: Cannot read property 'kids' of undefined
The model looks like this:
Affiliate = new Schema({
'name': String,
'address': String,
'features': {
'kids': { type: Boolean, default: false },
}
});
My form fields looks like this. They are used for both creating and updating with extra fields added for updating:
<input type="text" name="a[name]" id="a[name]" />
<input type="text" name="a[address]" id="a[address]" />
<input <% if (a.features.kids) { %>checked <% }; %>type="checkbox" name="a[features.kids]" id="a[features.kids]" />
Code for creating a new item. This works fine and all the information is added properly:
var a = new Affiliate(req.body.a);
a.save(function() {
res.redirect('/admin');
});
Broken code for updating an item:
Affiliate.findOne({ _id: req.params.id}, function(err, a) {
if (!a) return next(new NotFound('Affiliate not found'));
a.name = req.body.a.name;
a.address = req.body.a.address;
a.features.open_gym = req.body.a.features.kids;
a.save(function(err) {
res.redirect('/admin');
});
});

Yeah, I think the express bodyParser is probably not interpreting a[features.kids] the way you expect. You can access the field with req.body.a['features.kids'] since it's not interpreting that embedded period. Try naming your <input> a[features][kids] and see if that parses into the object structure you are expecting.

Related

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.

Can't Edit and Update properties with form Reactjs and MongoDB

So I'm using Nodejs, MongoDB and Reactjs
and I'm trying to Edit properties of projects.
I have multiple projects and when I want to edit properties of one I can't do it. We can access to properties inside inputs, we can see Title and Type but can't even delete, write, he access to properties by its ID but then I can't change it, I guess I have multiple problems here than.
I'll write here my server code, and my Edit/Update project page and a gif with an example when I say that I can't even change anything on inputs.
My server code:
//Render Edit Project Page byId
app.get('/dashboard/project/:id/edit', function(req, res){
let id = req.params.id;
Project.findById(id).exec((err, project) => {
if (err) {
console.log(err);
}
res.json(project);
});
}
//Update Projects Properties byId
app.put('/dashboard/project/:id/edit', function(req, res){
var id = req.params.id;
var project = {
title: req.body.title,
typeOfProduction: req.body.typeOfProduction
};
Project.findByIdAndUpdate(id, project, {new: true},
function(err){
if(err){
console.log(err);
}
res.json(project);
})
};
My React Component Edit Project Page
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import './EditProject.css';
class EditProject extends Component {
constructor(props){
super(props);
this.state = {
//project: {}
title: '',
typeOfProduction: ''
};
}
inputChangedHandler = (event) => {
const updatedProject = event.target.value;
}
componentDidMount() {
// console.log("PROPS " + JSON.stringify(this.props));
const { match: { params } } = this.props;
fetch(`/dashboard/project/${params.id}/edit`)
.then(response => { return response.json()
}).then(project => {
console.log(JSON.stringify(project));
this.setState({
//project: project
title: project.title,
typeOfProduction: project.typeOfProduction
})
})
}
render() {
return (
<div className="EditProject"> EDIT
<form method="POST" action="/dashboard/project/${params.id}/edit?_method=PUT">
<div className="form-group container">
<label className="form--title">Title</label>
<input type="text" className="form-control " value={this.state.title} name="title" ref="title" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container">
<label className="form--title">Type of Production</label>
<input type="text" className="form-control " value={this.state.typeOfProduction} name="typeOfProduction" ref="typeOfProduction" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container button">
<button type="submit" className="btn btn-default" value="Submit" onClcik={() => onsubmit(form)}>Update</button>
</div>
</form>
</div>
);
}
}
export default EditProject;
Erros that I have:
1- DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.
2- Inputs can't change
3- When click "Update" button:
I think your update override the entire object because you forgot the $set operator. This is the operator to change only the atributtes of an object and not the entire object replacing!
Example:
Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
First of all, concerning the deprecation warning, you need to change the method findAndModify (As I do not see it here, I guess you're using it elsewhere, or maybe one of the methods you use is calling it) by one of the suggested methods and change your code accordingly.
Then, you need to learn about React and controlled components : https://reactjs.org/docs/forms.html
You need to set the component's state in your onChange handler, such as :
this.setState({
title: event.target.value // or typeOfProduction, depending on wich element fired the event
});
This is called a controlled component in React.
Concerning the response body you get when clicking on Update button, this is actually what you asked for :
res.json(project);
returns the project variable as a JSON file, which is displayed on your screenshot.
See this question for more information about it : Proper way to return JSON using node or Express
Try replace "value" in input tag with "placeholder"

How search value inside an object in Redis?

I'm newbie to Redis and Nodejs, I've watched this tutorial and i'm trying to search users by name,
Here is the object, returned from redis when i pass the id:
{
first_name: '‪john',
last_name: 'doe',
email: 'john#gmail.com',
phone: '543313305',
id: 'user001'
}
Here is the function of the search :
app.post('/user/search',function (req,res,next) {
let id = req.body.id;
client.hgetall(id ,function(err,obj){
if(!obj){
res.render('searchusers',{
error:"user doesn't exist",
});
} else {
obj.id = id
console.log(obj);
res.render('details',{
user:obj, });
}
});
});
I've tried to replace the search by id to search by first_name by doing this:
First I've changed the field name to "first_name" instead of "id"
<h1>Search Users</h1>
{{#if error}} <span>{{error}}</span>{{/if}}
<form class="form-inline" method="POST" action="/user/search">
<div class="form-group">
<input type="text" name="first_name" placeholder="Search" class="form-
control">
</div>
<input type="submit" class="btn btn-primary" value="Search">
And than I've changed it in the app.js ;
app.post('/user/search',function (req,res,next) {
let first_name = req.body.first_name;
client.hgetall(first_name ,function(err,obj){
if(!obj){
res.render('searchusers',{
error:"user doesn't exist",
});
} else {
obj.first_name = first_name
console.log(obj);
res.render('details',{
user:obj, });
}
});
});
The hgetall method that you are using in the search function of the method looks up a hash by the key, which in this case is the user id and returns all of the fields of the hash from Redis.
There is no function to search over the fields of the hash. If you need to be able to map from a first name to a user, you will need to manually build a secondary index using a data structure such as sets, that allows you to go from first name to the users that have that first name.

Why am I getting wrappedPointCut instead of the data?

I have 3 tiers of nested objects stored in mongoDB.
The first tier shows the name like it is supposed to, the second as well.
But when it goes in to the third tier it shows the text:"wrappedPointCut"
Why is this? and What does wrapedPointCut mean??
I have a handlebars test code like this:
{{#each tierOne}}
<h2>{{ this.tierOneName }}</h2>
{{#each tierTwo}}
<h2>{{ this.tierTwoName }}</h2>
{{#each tierThree}}
<h2>{{ this.tierThreeName }}</h2>
{{/each}}
{{/each}}
{{/each}}
</div>
When iterating through the nested objects in JS I get the correct output in console. Also if I add 4 objects to the third tier I get 4 headers that read:"wrapedPointCut". This must mean it knows that there is data here.
this is the mongoDB structure:
this is the Tier 3:
var TierThree = new mongoose.Schema({
tierThreeName : {
type: String
}
});
this is the Tier 2:
var TierTwo = new mongoose.Schema({
tierTwoName : {
type: String
},
tierThree : [TierThree]
});
this is the Tier 1:
var TierOne = mongoose.Schema({
tierOneName : {
type: String,
index:true
},
tierTwo: [TierTwo]
});
Here is the function that exports the third tier to monngoDB:
module.exports.createTierThree = function(newTierThree, tierOne, tierTwoName , callback){
for (var i = 0; i < tierOne.tierTwo.length; i++) {
if(tierOne.tierTwo[i].tierTwoName == tierTwoName ){
tierOne.tierTwo[i].tierThree.push(newTierThree);
tierOne.tierTwo[i].save(function (err) {
if (!err) console.log('Success!');
});
}
}
};
The problem seemed to be that I'm doing something wrong when adding to database. If I add to the database using shell command everything is fine. So the problem now is to turn mongoDB shell command to JS.

How to catch the object_id of a post submit in Node.js (MongoJS)?

I have this code in app.js where I insert the content of the title and content fields to their respective document fields in MongoDB:
//post to 'post/new'..
app.post('/post/new', function(req, res){
//get the `title` and `content` fields & save them as variables to be later reused (they rely on the `name:` values).
var title = req.body.title;
var content = req.body.content;
//call the database and find the `_id` to be used in the redirect later..
db.local.find({_id: ObjectId(req.params.id)}, function(id) {});
//insert the title and content in the database (taken from the submit form)
db.local.insert ( {title: title, content: content},
//not sure if I really need to define an err&doc right now so to be ignored..
function(err, doc) {
//redirect to "localhost.com/post/(post)_id/(post)title"
res.redirect('/post/'+req.params.id+'/'+req.body.title);
});
});
This is what I have on post_new.ejs:
<form method="post">
<div>
<div>
<span>Title :</span>
<input type="text" name="title" id="editPostTitle" />
</div>
<div>
<span>Content :</span>
<textarea name="content" rows="20" id="editPostBody"></textarea>
</div>
<div id='editPostSubmit'>
<input type="submit" value="Send" />
</div>
</div>
</form>
The problem is that I get all but no _id in the res.redirect to work, meaning, the title works wonderfully, but the _id no..
How can I get the object id to work in the redirect? Thank you!
EDIT
This is the problem I get and think it's unrelated..but I'll include it for the full view of the issue.
500 Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
Assuming you are using the native node MongoDB driver the callback of the insert function returns the array of inserted objects.
The example in those docs is a pretty good one. Adjusted for your example, it would like something like this:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
db.collection('local').insert({title: title, content: content}, function(err, docs) {
console.log('Inserted _id is '+docs[0]._id);
});
});
Regarding your error, it sounds like req.params.id is not a valid BSON ObjectId.

Resources