MethodOverride PUT not working - javascript

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.

Related

Creating new array with variables coming back as undefined

I am creating a blog web application. I currently have the blogSchema set up as
var BlogSchema = new mongoose.Schema({
title: String,
image: String,
body: String,
created:
{type: Date, default: Date.now},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Blog", BlogSchema);
I have a post form of the following
<div class="ui main text container segment">
<div class="ui huge header">New Blog</div>
<form class="ui form" action="/blogs" method="POST">
<div class="field">
<label>Title</label>
<input type="text" name="blog[title]" placeholder="title">
</div>
<div class="field">
<label>Image</label>
<input type="text" name="blog[image]" placeholder="image">
</div>
<div class="field">
<label>Blog Content</label>
<textarea name="blog[body]"></textarea>
</div>
<input class="ui violet big basic button" type="submit">
</form>
</div>
On my blog routes page I have my create route as
router.post("/blogs", function(req, res){
// create blog
//req.body.blog.body = req.sanitize(req.body.blog.body);
var title = req.body.title;
var image = req.body.image;
var desc = req.body.body;
var author= {
id: req.user._id,
username: req.user.username
}
var newBlogpost = {title: title, image: image, body: desc, author: author}
Blog.create(newBlogpost, function(err, newBlog){
if(err){
res.render("blogs/new");
} else {
//then, redirect to the index
console.log(newBlogpost)
res.redirect("/blogs");
}
});
});
However when I sign in on the web application and complete the form and view my commmand line for the return of console.log(newBlogpost) after I submit, I get:
{ title: undefined,
image: undefined,
body: undefined,
author: { id: 5a0cbcc3d6c7070a7bb6c45e, username: 'cat' }
I am not sure why the new variable array I am creating has these three variable as undefinded and would appreciate help.

Delete item from nested array in Schema using moongoose

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.

ERROR SyntaxError: Unexpected token < in JSON at position 0

ERROR:
core.es5.js?0445:1084 ERROR SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse (<anonymous>
SITUATION:
What I am trying to achieve is to pre-select the choice the user already made previously and prevent him from voting twice.
CODE:
component.html
<article class="panel panel-default">
<div class="panel-body">
{{ poll.title }}
<br>
<br>
<form #form="ngForm">
<fieldset [disabled]="alreadyVotedFor(-1)">
{{ poll.counter1 }} votes <input type="radio" id="{{ poll.choice1 }}" name="my_radio" value="{{ poll.choice1 }}" (click)="onChoice1(form)" [checked]="alreadyVotedFor(1)"> {{ poll.choice1 }}
<br>
{{ poll.counter2 }} votes <input type="radio" id="{{ poll.choice2 }}" name="my_radio" value="{{ poll.choice2 }}" (click)="onChoice2(form)" [checked]="alreadyVotedFor(2)"> {{ poll.choice2 }}
</fieldset>
</form>
</div>
<footer class="panel-footer">
<div class="author">
{{ poll.username }}
</div>
<div class="config" *ngIf="belongsToUser()">
<a (click)="onEdit()">Edit</a>
<a (click)="onDelete()">Delete</a>
</div>
</footer>
</article>
component.ts
votes: any;
ngOnInit() {
this.pollService.voted(this.poll, localStorage.getItem('userId')).subscribe(
data => {
this.votes = data.votes;
console.log("NGONINIT this.votes: "+ this.votes);
},
err => { console.log("NGONINIT ERROR: "+ err) },
() => { console.log("SUBSCRIBE COMPLETE this.votes: "+ this.votes); }
);
}
alreadyVotedFor(choice: number) {
let result = "";
if (this.votes) {
console.log("THIS.VOTES: "+this.votes);
for (var i = 0; i < this.votes.length; i ++) {
if (this.votes[i].poll == this.poll.pollId) {
result = "disabled";
if (this.votes[i].choice == choice) {
result = "selected";
}
}
}
}
return result;
}
service
voted(poll: Poll, userID: string) {
return this.http.get('http://localhost:3000/'+userID)
.map(response => response.json());
}
routes/user.js
router.get('/:userid', function (req, res, next) {
var userId = req.params.userid;
User.findById(userID, function (err, user) {
console.log("USER JSON? :"+user.json());
return res.send(user.json());
});
});
models/user
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var schema = new Schema({
firstName: {type: String, required: true},
lastName: {type: String, required: true},
password: {type: String, required: true},
email: {type: String, required: true, unique: true},
polls: [{type: Schema.Types.ObjectId, ref: 'Poll'}],
votes: [{
poll: {type: Schema.Types.ObjectId, ref: 'Poll'},
choice: {type: Number},
}],
});
schema.plugin(mongooseUniqueValidator);
module.exports = mongoose.model('User', schema);
This error happens when server responds with HTML instead of JSON, particularly when a route is not found and the response is 404 page.
It's unlikely that API has the only route which is mounted at root and available as http://localhost:3000/'+userID, just because it's not possible for the server to do anything else in this case.
If the route is mounted to some path, like
app.use('/user', userRoutes);
then the requests should be performed to this path as well
return this.http.get('http://localhost:3000/user/'+userID)

How to Get Params from Http Post and Insert

I am trying to capture user entered credentials and use them as a parameter to query a database. Unfortunately, I am a little lost on how to code that process. I am using angular,express, node, jQuery, and html. I am not very experienced with angular, node, and jQuery, so forgive me if this is something very simple; I am here to learn.
Here is the html where the forms live:
<!DOCTYPE html >
<html ng-app="token">
<%include header%>
<%include navbar%>
<div ng-controller="TokenCtrl">
<form ng-submit="submitLogin(loginForm)" role="form" ng-init="loginForm = {}">
<div class="form-group">
<label>email</label>
<input type="email" name="email" ng-model="loginForm.email" required="required" class="form-control"/>
</div>
<div class="form-group">
<label>password</label>
<input type="password" name="password" ng-model="loginForm.password" required="required" class="form-control"/>
</div>
<button class="btn btn-primary btn-lg" ng-click="handleLoginBtnClick()">Sign in</button>
</form>
</div>
</body>
Here is the JS for the TokenCtrl and token module, which is a derivative of ng-token-auth:
var a = angular.module('token', ['ng-token-auth']);
a.config(function($authProvider) {
// the following shows the default values. values passed to this method
// will extend the defaults using angular.extend
$authProvider.configure({
apiUrl: '/users',
tokenValidationPath: '/auth/validate_token',
signOutUrl: '/auth/sign_out',
emailRegistrationPath: '/auth',
accountUpdatePath: '/auth',
accountDeletePath: '/auth',
confirmationSuccessUrl: window.location.href,
passwordResetPath: '/auth/password',
passwordUpdatePath: '/auth/password',
passwordResetSuccessUrl: window.location.href,
emailSignInPath: '/auth/sign_in/:email/:password',
storage: 'cookies',
forceValidateToken: false,
validateOnPageLoad: true,
proxyIf: function() { return false; },
proxyUrl: '/proxy',
omniauthWindowType: 'sameWindow',
tokenFormat: {
"access-token": "{{ token }}",
"token-type": "Bearer",
"client": "{{ clientId }}",
"expiry": "{{ expiry }}",
"uid": "{{ uid }}"
},
cookieOps: {
path: "/",
expires: 9999,
expirationUnit: 'days',
secure: false,
domain: 'domain.com'
},
createPopup: function(url) {
return window.open(url, '_blank', 'closebuttoncaption=Cancel');
},
parseExpiry: function(headers) {
// convert from UTC ruby (seconds) to UTC js (milliseconds)
return (parseInt(headers['expiry']) * 1000) || null;
},
handleLoginResponse: function(response) {
return response.data;
},
handleAccountUpdateResponse: function(response) {
return response.data;
},
handleTokenValidationResponse: function(response) {
return response.data;
}
});
});
a.controller('TokenCtrl', function($scope, $auth) {
$scope.handleRegBtnClick = function() {
$auth.submitRegistration($scope.registrationForm)
.then(function(resp) {
// handle success response
})
.catch(function(resp) {
// handle error response
});
};
$scope.handlePwdResetBtnClick = function() {
$auth.requestPasswordReset($scope.pwdResetForm)
.then(function(resp) {
// handle success response
})
.catch(function(resp) {
// handle error response
});
};
$scope.handleLoginBtnClick = function() {
$auth.submitLogin($scope.loginForm)
.then(function(resp) {
// handle success response
})
.catch(function(resp) {
// handle error response
});
};
$scope.handleSignOutBtnClick = function() {
$auth.signOut()
.then(function(resp) {
// handle success response
})
.catch(function(resp) {
// handle error response
});
};
});
On the run of this function, it leads to this url:
'/auth/sign_in/:email/:password'
Using Express, I route this url to another function. Here is the route code:
app.post('/users/auth/sign_in/:email/:password', routes.verifyusers);
Which leads to,
exports.verifyusers= function(req, res) {
models.user.find({
where: {
email: req.params.email,
password: req.params.password
}
}).then(function(user) {
if(user) {
console.log("alright !")
};
});
};
When the code runs, this is what I get in the console:
Executing (default): SELECT "id", "username", "email", "password", "createdAt", "updatedAt" FROM "users" AS "user" WHERE "user"."email" = ':email' AND "user"."password" = ':password' LIMIT 1;
:email
:password
This is result is irrespective to the form data.
I think the problem is coming from emailSignInPath: '/auth/sign_in/:email/:password',
You should try with
// config
emailSignInPath: '/auth/sign_in'
// route declaration
app.post('/users/auth/sign_in', routes.verifyusers);
// route action
exports.verifyusers = function(req, res) {
models.user.find({
where: {
email: req.body.email,
password: req.body.password
}
}).then(function(user) {
if(user) {
console.log("alright !")
};
});
};
ps: don't forget to declare a body parser in your app app.use(express.bodyParser())

Error copying req.body properties into Mongoose Model

First of all I have to say that I'm new in Angular and node technologies. So sorry for my ignorance.
I get this error when I try to save an Entity from edition view: 'Cast to ObjectId failed for value "[object Object]" at path "category"'.
Well, I've got these code:
HTML:
<form class="form-horizontal" data-ng-submit="update()" novalidate>
<fieldset>
<div class="form-group">
<label for="listaCat">Categoría:</label>
<select id="listaCat" class="form-control" data-ng-Fmodel="notification.category" data-ng-options="c.name for c in listaCategorias track by c._id">
</select>
</div>
<div class="form-group">
<label class="control-label" for="name">Descripción</label>
<div class="controls">
<input type="text" data-ng-model="notification.name" id="name" class="form-control" placeholder="Descripción" required>
</div>
</div>
<div class="form-group">
<input type="submit" value="Guardar" class="btn btn-default">
</div>
<div data-ng-show="error" class="text-danger">
<strong data-ng-bind="error"></strong>
</div>
</fieldset>
</form>`
Angular controller:
$scope.update = function() {
var notification = $scope.notification;
notification.$update(function() {
$location.path('notifications/' + notification._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Server side controller:
var mongoose = require('mongoose'),
errorHandler = require('./errors.server.controller'),
Notification = mongoose.model('Notification'),
_ = require('lodash');
exports.update = function(req, res) {
var notification = req.notification;
notification = _.extend(notification , req.body);
notification.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(notification);
}
});
};
Mongoose Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var NotificationSchema = new Schema({
name: {
type: String,
default: '',
required: 'Rellena la notificación',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
category: {
type: Schema.ObjectId,
ref: 'Category'
}
});
mongoose.model('Notification', NotificationSchema);
var CategorySchema = new Schema({
name: {
type: String,
default: '',
required: 'Rellena la categoría',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Category', CategorySchema);
So, if I debug inside Server controller at update method with WebStorm, I can see that req.body comes with each attribute well formed, but after convert req.body into Notification Mongoose Model with:
notification = _.extend(notification , req.body);
the category attribute is not a Model but an ObjectId. It seems as lodash.extend is not working properly for complex attributes. I've tried many other ways of cloning the object but without success.
Finally I solved it, with this line inside the angular controller:
notification.category = $scope.notification.category._id;
notification.$update(function() {
Anyway, I think that this is not the right way. I guess there must be a way of copying the req.body properties into a mongoose model without doing it manually for the complex properties.
Thanks a lot in advance!
Since you are working on AngularJS and ExpressJS, i would suggest you to use $resource service which is exactly meant for interacting with the rest API.
**$resource** contains these default set of actions:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
There is nice documentation available in the link that i shared above.
In your case:
i assume, http://localhost:300/notifications/:id, this might be your rest url where you want to perform update action.
You can create your custom services like:
var module = angular.module('myapp.services',['ngResource']);
module.factory('MyAppUpdateService',function($resource){
return $resource('notifications/:id',
{
id: '#id'
},
{
'update': { method:'PUT' }
}
);
});
Now inside your angular app controller you can inject this service as dependency and hence it will be available to perform update in that REST url.
angular.module('myapp',['ngResource','myapp.services']);
angular.module('myapp').controller('MeetupsController',['$scope','$resource','$state','$location','MeetupUpdateService','socket',
function($scope,$resource,$state,$location, MyAppUpdateService){
$scope.updateMeetup = function(){
$scope.updateService = new MyAppUpdateService();
$scope.updateService.name = $scope.notification.name;
.
.
.
$scope.updateService.$update({id:$scope.notification.category._id},function(result){
$location.path("/meetup/")
});
}
})]);
So this was just an example, if you want more comprehensive implementation. Look here, i am creating a MEAN seed of my own, and i am doing the same.
Any doubt please do ask.

Categories

Resources