Delete item from nested array in Schema using moongoose - javascript

I need help with removing this item from nested array. I tried used $http.delete but this method deleted whole ObjectID from database, and second problem is that I can't connect the click on the "removing" button with backend code.
My code:
var product = new Schema({
title: String,
price: String,
description: [ObjectID]
});
Index.html
<form name="editProduct.descriptionForm" ng-submit="editProduct.updateDescription(newDescription, editProduct.descriptionForm.description.$valid)" novalidate>
<div ng-class="{ 'has-success':(editProduct.descriptionForm.description.$valid && !editProduct.descriptionForm.description.$pristine), 'has-error':(!editProduct.descriptionForm.description.$valid && !editProduct.descriptionForm.description.$pristine) || (!descriptionForm.description.$valid && descriptionForm.$submitted) }">
<div class="entry input-group" ng-repeat="item in newDescription track by $index">
<strong><input ng-disabled="editProduct.disabled" class="form-control" type="text" name="description" ng-model="newDescription[$index]" required></strong>
<span class="input-group-btn">
<a ng-click="editProduct.deleteDescription(item);" class="btn btn-remove btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
</div>
</div>
<br>
<button ng-disabled="editProduct.disabled" class="btn btn-primary" type="submit">Save</button>
</form>
routes.js
router.put('/editProduct', function(req, res){
var editProduct = req.body._id;
var options = { multi: true };
Product.findOneAndUpdate({ _id: editProduct }, { $pull: { description: req.params.description }}, options, function(err, product){
if(err) throw err;
if(!product){
res.json({ success: false, message: 'Product no found' });
} else {
product.update(function(err){
if(err){
console.log(err);
} else {
res.json({ success: true, message: 'Description removed!'})
}
});
};
});
});
I also tried the following approach:
Product.findOne({'_id' : product.id}, function(err, me){
for(var i=0; i<=me.description.length; i++){
if (String(me.description[i])==String(uid)){
me.description.remove(uid);
me.save();
}
}
});
I think, the biggest problem is that I don't how to connect this function to the button.

Please try console.log(req.params.description) Before the Mongoose update query and check if the output is indeed a valid ObjectId.
If the console output is not showing the valid uid, then the problem is in the angular code. Most probably in editProduct.deleteDescription(item) function. Check if you are making Http Request by passing the correct Description Id as the parameter. Thats probably something like item.descriptionId or item.id. Debug thoroughly.

Related

Get Specific Object from Array in MongoDB collection into handlebars table

I'm trying to build a table with values from an Array of objects from mongodb, but I only can get every value and only need the specific value.
So I made a query like this
router.get("/arquiveExpense", ensureAuthenticated, (req, res) => {
House.find({
userID: req.user.id,
expensesHouse: { $elemMatch: { status: "Private" } }
}).then(house => {
console.log(house);
res.render("houses/arquiveExpense", {
house: house
});
});
});
I want to retrieve the specific value from expensesHouse with status 'Private'.
And in handlebars I have this structure
<tbody>
<tr>
{{#each house}}
<td>{{expenseType}}</td>
<td>{{price}}€</td>
<td>{{payAt}}</td>
<td>{{formatDate date 'MMMM Do YYYY'}}</td>
<td>
<a href="/houses/showExpense/{{id}}" id="detailsExpense"
class="btn btn-outline-light mb-3"><i class="fas fa-eye mr-2"></i>Details
</a>
<td>
<a href="/houses/editExpense/{{id}}" id="editExpense" class="btn btn-outline-light mb-3"><i
class="fas fa-edit mr-2"></i>Edit
</td>
</tr>
{{else}}
<p>No expenses</p>
{{/each}}
</tbody>
And after this handlebars, this is the result
The Schema structure is the follow
So I want to show in the web page the value from expensesHouse with the Status 'Private'.
How can I change my code to retrieve only this value ?
Please update your code with the below, it resolves your issues with code & also with query :
router.get("/arquiveExpense", ensureAuthenticated, async (req, res) => {
try {
let house = await House.findOne({ // You can use findOne as if userID is unique
userID: req.user.id
}, { expensesHouse: { $elemMatch: { status: "Private" } } }) //$elemMatch in projection helps you to get what is needed
if (house) { // .findOne() returns null if no document found, else an object
console.log(house);
res.render("houses/arquiveExpense", {
house: house
});
} else { // No document found
console.log('No house found')
}
} catch (error) {
console.log('Error at DB call ::', error)
}
})

MethodOverride PUT not working

I am using Node.js, Express and MethodOverride to try and have a form update only 1 part of a model (my user model).
User model:
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
password: String,
profile: {
name: { type: String, default: 'Company Name' },
location: { type: String, default: 'Location' },
website: { type: String, default: 'Your Website' },
picture: { type: String, default: '' }
},
assetNumPre: { type: String, default: 'test' }, // this is the one I want to change
});
module.exports = mongoose.model('User', userSchema);
HTML form:
<form role="form-inline"action="/dashboard/settings/assetNumber?_method=PUT" method="POST">
<div class="col-md-3">
<div class="form-group">
<label for="prefix" class="control-label">Prefix for Asset Number</label>
<br>
<small>Any alphanumeric characters to a limit of 6</small>
<input type="text" class="form-control" id="prefix" name="prefix" placeholder="Prefix max 6 characters" maxlength="6" value="{{ prefix }}">
</div><!-- Prefix for Asset Number-->
<br>
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
Then route:
app.put('/dashboard/settings/assetNumber',
setRender('dashboard/settings/assetNumbers'),
setRedirect({auth: '/login'}),
isAuthenticated,
dashboard.getDefault,
(req, res) => {
var prefix = req.body.prefix;
console.log(req.params);
User.findByIdAndUpdate({_id: req.params.user_id}, prefix, function(err, UpdatedUser) {
if (err) {
res.send(err);
}
console.log(UpdatedUser);
});
res.locals.prefix = req.user.assetNumPre;
});
One thing my route is missing is req.user.assetNumPre which is where I need to save it but I have no clue how to do this PUT request. Docs are not helping much either.
I got the route from a Stack Overflow example a few days ago and can't find the link to it. My app.js had method override working because I have done DELETE requests already. The model has the correct field and has a default test value that shows up in my show page.
You're calling this:
User.findByIdAndUpdate({_id: req.params.user_id}, prefix...
But prefix is only the value:
var prefix = req.body.prefix;
findByIdAndUpdate takes an Object, not a value, to update a specific field.
So try:
User.findByIdAndUpdate({_id: req.params.user_id}, { assetNumPre: prefix }...
Here is the fixed route:
app.put('/dashboard/settings/assetNumber',
setRedirect({auth: '/login', success: '/dashboard/settings/assetNumber', failure: '/dashboard/settings/assetNumber'}),
isAuthenticated,
(req, res) => {
User.findById(req.user.id, function(err, user) {
if (err) return (err);
user.assetNumPre = req.body.prefix || 'pre';
user.save(function(err) {
if (err) return (err);
req.flash('success', { msg: 'Asset Number Prefix updated.' });
res.redirect(req.redirect.success);
});
});
res.locals.prefix = req.user.assetNumPre;
});
So a few things changed that were not part of the issue. I figured out I need to just set the data inside the callback function. Then do a user.save.

PUT Request parse error

I have a website where a user stores resources and makes articles about them. At this time, i'm building the comments section where users can make comments in the articles. I have a put request which is triggered from controller which sends the id of the article and the comment.
but i receive the following error,
SyntaxError: Unexpected token 1
at parse (/home/themis/webappionio/node_modules/body-parser/lib/types/json.js:83:15)
at /home/themis/webappionio/node_modules/body-parser/lib/read.js:116:18
at invokeCallback (/home/themis/webappionio/node_modules/raw-body/index.js:262:16)
at done (/home/themis/webappionio/node_modules/raw-body/index.js:251:7)
at IncomingMessage.onEnd (/home/themis/webappionio/node_modules/raw-body/index.js:308:7)
at emitNone (events.js:67:13)
at IncomingMessage.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:905:12)
at doNTCallback2 (node.js:441:9)
at process._tickCallback (node.js:355:17)
here is my server.js:
//defining Articles Model
var ArticleSchema = mongoose.Schema({
_creatorname: String,
title : String,
body : String,
resource: String,
published : String,
comments: [{
_commentorname: String,
content : String,
date : String
}]
});
var Articles = mongoose.model("Articles", ArticleSchema);
//pushing comment into the specific article
app.put("/home/:id", function(req,res){
var _commentorname = req.user.username;
var content = req.body.comment;
var date = moment().tz("Europe/Athens").format("DD/MM/YY HH:mm");
Articles.findByIdAndUpdate(
id,
{$push: {"comments" : {_commentorname: _commentorname, content: content, date: date}}},
{safe: true, upsert: true, new : true},
function(err, article) {
console.log(err);
}
);
});
My controller :
$scope.addComment = function(id, comment) {
console.log(id);
console.log(comment);
$http.put("/home/" + id, comment)
.success(function(response){
$scope.all();
});
};
and my html form:
<div class="panel-footer">
<input type="text" id="userComment" ng-model="comment" class="form-control input-sm chat-input" placeholder="Write your message here..." />
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" ng-click="addComment(article._id, comment)"><span class="glyphicon glyphicon-comment"></span> Add Comment</button>
</span>
</div>
Here is my final server.js
app.put("/home/:id", function(req,res){
var id = req.params.id;
Articles.findByIdAndUpdate(
id,
{$push: {"comments" : {_commentorname: req.user.username, content:req.body.comment}}},
{safe: true, upsert: true, new : true},
function (err, results) {
if (err)
{
res.send("there was a problem updating the information: " + err);
}
else {
res.format({
json: function() {
res.json(results);
}
});
}
}
);
});
The problem is that comment here is not actually being sent as an Object but just as a "string". So you need to structure the Object instead.
So correct the angular controller code:
$scope.addComment = function(id, comment) {
console.log(id);
console.log(comment);
$http.put("/home/" + id, { "comment": comment})
.success(function(response){
$scope.all();
});
};
New Answer:
Actually it looks like that you are submitting in the Angular controller the comment variable in a "raw" way instead of sending it as JSON, you need to change it to the
$scope.addComment = function(id, comment) {
console.log(id);
console.log(comment);
$http.put("/home/" + id, {comment: comment})
.success(function(response){
$scope.all();
});
};
Old Answer:
I think it throws the error because you format the date to the unusual format "DD/MM/YY HH:mm", if you would use native JS object it would work, e.g.
var date = moment().tz("Europe/Athens").toDate()

M.E.A.N put not updating

I have an array of comments on a picture called 'comments'
I also have a property called "newcmt" that i'd like to push into comments
I have a function addComment that tries and fails to make these changes.
I have tried a few different ways, but the put doesn't seem to update the api despite returning the proper changes in the response.
Am I just updating the reference?
Is asynchronous nature causing errors?
Is my mongo scheme wrong?
Would it be easier to make comments its own endpoint?
JS
var refresh = function() {
$http.get('/api/things').success(function(awesomeThings) {
$scope.awesomeThings = awesomeThings;
});
};
$scope.addThing = function() {
if($scope.newThing === '') {
return;
}
$http.post('/api/things/', { name: $scope.newThing });
refresh();
};
$scope.addComment = function(thing) {
if(thing.newcmt == '') {
return;
}
$scope.newcmt = thing.comment;
$scope.newcmt.push(thing.newcmt);
console.log($scope.newcmt)
$http.put('/api/things/' + thing._id, {comment : $scope.newcmt }, {safe: true, upsert: true, new : true}).success(function(response) {
console.log(response);
refresh();
})
};
$scope.deleteThing = function(thing) {
$http.delete('/api/things/' + thing._id);
refresh();
};
HTML
<div class="container">
<img class="img-responsive" src="{{thing.url}}" alt="">
</div>
<div class="col-md-12" ng-repeat="comment in thing.comment">
<p>{{comment}}</p>
</div>
<div class="row">
<div class="col-xs-12">
<i class="fa fa-comment-o fa-3x"></i>
<input type="text" placeholder='...' ng-model="thing.newcmt" >
<button class="btn btn-default" ng-click="addComment(thing)">+</button>
<i class="fa fa-arrow-down fa-2x pull-right"></i>
<i class="fa fa-arrow-up fa-2x pull-right"></i>
</div>
</div>
MODEL
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ThingSchema = new Schema({
name: String,
url: String,
credit: String,
active: Boolean,
comment: Array,
newcmt: String
});
module.exports = mongoose.model('Thing', ThingSchema);
PUT
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.status(404).send('Not Found'); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.status(200).json(thing);
});
});
};
full github:
https://github.com/jneljneljnel/meangen

How to create inputs that you can keep adding to a collection in meteor

I am trying to figure out how to create a form that has a input field, as well as a "+" button to add another input field. I than want to take those inputs and insert them into a collection. Than output them as a list. Basically I want to give the option to add as many list items as possible. If there is a better way to do this instead of a "+" button to add input fields,I' open to any suggestions.
code so far
HTML
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
<template name="postItem">
<h2>{{service}}</h2>
<ul><li>{{task}}</li></ul>
</template>
<template name="postSubmit">
<form>
<label for="service">Add a Service</label>
<input name="service" type="text" value="" placeholder="Service Type"/>
<label for="task">Add a task (spaces between each kind)</label>
<input name="task" type="text" value="" placeholder="type or task for service"/>
<input type="submit" value="Submit" />
</form>
</template>
JS
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
service: $(e.target).find('[name=service]').val(),
task: $(e.target).find('[name=task]').val()
};
Meteor.call('post', post, function(error, id) {
if (error)
return alert(error.reason);
Router.go('postPage', {_id: id});
});
}
});
Posts = new Meteor.Collection('posts');
Posts.allow({
update: ownsDocument,
remove: ownsDocument
});
Posts.deny({
update: function(userId, post, fieldNames) {
// may only edit the following two fields:
return (_.without(fieldNames, 'service', 'task').length > 0);
}
});
Meteor.methods({
post: function(postAttributes) {
var user = Meteor.user(),
postWithSameLink = Posts.findOne({url: postAttributes.url});
// ensure the user is logged in
if (!user)
throw new Meteor.Error(401, "You need to login to post new stories");
// ensure the post has a service
if (!postAttributes.service)
throw new Meteor.Error(422, 'Please fill in a service');
// ensure the post has a task
if (!postAttributes.task)
throw new Meteor.Error(422, 'Please fill in a task');
// check that there are no previous posts with the same link
if (postAttributes.url && postWithSameLink) {
throw new Meteor.Error(302,
'This link has already been posted',
postWithSameLink._id);
}
// pick out the whitelisted keys
var post = _.extend(_.pick(postAttributes, 'service', 'task'), {
userId: user._id,
author: user.username,
submitted: new Date().getTime()
});
var postId = Posts.insert(post);
return postId;
}
});
Template.postsList.helpers({
posts: function() {
return Posts.find({}, {fields: {service: 1, task: 1}}).map(function(post, index) {
return post;
});
}
});

Categories

Resources