Can't update a single doc in Mongo with Node - ajax

Hopefully someone can point out my error here.
In my app a user clicks on a button to insert a doc into the database. When they click on another button, a timestamp is added to an array.
Here's the code to create the doc (it works):
// Add User
function addUser(event) {
event.preventDefault();
ident = makeWords(2);
var newUser = {
'ident' : ident,
'group': '',
'timestamps': [],
'date_created': Date()
}
// Use AJAX to post the object to our adduser service
$.ajax({
type: 'POST',
data: newUser,
url: '/users/adduser',
dataType: 'JSON'
}).done(function( response ) {
if (response.msg === '') {
console.log('user added');
} else {
alert('Error');
}
});
};
And here's the route which handles it:
/*
* POST to adduser.
*/
router.post('/adduser', function(req, res) {
var db = req.db;
var collection = db.get('testcol'); //'testcol' is the name of my collection
collection.insert(req.body, function(err, result){
res.send(
(err === null) ? { msg: '' } : { msg: err }
);
});
});
I kind of thought that updating a doc would be just as easy. I'm grabbing the doc by the ident field, which will be unique to each user. However, I can't seem to make the client-side stuff pass to the server. Here's my client-side update:
function addError(event) {
event.preventDefault();
// If it is, compile all user info into one object
var errorUpdate = {
'$push': {'error_button': Date()}
}
// Use AJAX to post the object to our adduser service
$.ajax({
type: 'PUT',
data: errorUpdate,
url: '/users/errorUpdate',
dataType: 'JSON'
}).done(function( response ) {
if (response.msg === '') {
console.log("update sent, didn't receive an error");
}
else {
alert('Error');
}
});
};
This code executes, but the server-side just throws 500s. Here's that function:
/*
* update mongo doc
*/
router.put('/errorUpdate', function(req, res) {
var db = req.db;
var collection = db.get('testcol');
collection.update({'ident': ident},req.body, function(err, result){
if (err) {
console.log('Error updating menu: ' + err);
res.send({'users.js: error':'An error has occurred'});
} else {
console.log('doc has been updated');
res.send(item);
}
});
});
Any idea where I'm going wrong?

I solved this and it was a really really stupid mistake.
You might notice in my server-side code I use a variable called ident:
router.put('/errorUpdate', function(req, res) {
var db = req.db;
var collection = db.get('testcol');
collection.update({'ident': ident},req.body, function(err, result)...
ident is a global variable from my client-side stuff (global.js, which makes the ajax call), and it never made it to the server.
Further, I tried to send the Mongo update statement with the ident variable, which is totally unnecessary and just caused headaches.
Here's how I fixed it. This is client-side (where I only send the ident variable):
function addError(event) {
event.preventDefault();
// If it is, compile all user info into one object
var identifyMe = {
'ident': ident
}
// Use AJAX to post the object to our adduser service
$.ajax({
type: 'POST',
url: '/users/errors',
data: identifyMe,
dataType: 'JSON'
}).done(function( response ) {
// Check for successful (blank) response
if (response.msg === '') {
console.log('update sent, no errors received');
}
else {
console.log('Error detected. Response was: ' + response);
}
});
};
... and this is server-side, where I take that identifier and do the update (this works because all I'm doing is inserting a time stamp):
router.post('/errors', function(req, res) {
console.log(req.body);
var identifier = req.body.ident;
var db = req.db;
var collection = db.get('testcol');
collection.update({'ident': identifier}, {$push: {'error_button': Date()}}, function(err, result){
res.send(
(err === null) ? { msg: '' } : { msg: err }
);
});
});
You might notice that I'm pulling out that ident variable from the JSON that's being passed, with req.body.ident.
Hope this helps someone else struggling with updating a Mongo doc by posting to Express routes via Ajax with Node! :)

Related

Is there a way to show "Loading data.." option in Rally Grid(ext JS) while making the Ajax request to load the data?

I am trying to set the message to "Data Loading.." whenever the data is loading in the grid. It is working fine if I don't make an Ajax call. But, when I try to make Ajax Request, It is not showing up the message "Loading data..", when it is taking time to load the data. Can someone please try to help me with this.. Thanks in Advance.
_loadData: function(x){
var that = this;
if(this.project!=undefined) {
this.setLoading("Loading data..");
this.projectObjectID = this.project.value.split("/project/");
var that = this;
this._ajaxCall().then( function(content) {
console.log("assigned then:",content,this.pendingProjects, content.data);
that._createGrid(content);
})
}
},
_ajaxCall: function(){
var deferred = Ext.create('Deft.Deferred');
console.log("the project object ID is:",this.projectObjectID[1]);
var that = this;
console.log("User Reference:",that.userref,this.curLen);
var userObjID = that.userref.split("/user/");
Ext.Ajax.request({
url: 'https://rally1.rallydev.com/slm/webservice/v2.0/project/'+this.projectObjectID[1]+'/projectusers?fetch=true&start=1&pagesize=2000',
method: 'GET',
async: false,
headers:
{
'Content-Type': 'application/json'
},
success: function (response) {
console.log("entered the response:",response);
var jsonData = Ext.decode(response.responseText);
console.log("jsonData:",jsonData);
var blankdata = '';
var resultMessage = jsonData.QueryResult.Results;
console.log("entered the response:",resultMessage.length);
this.CurrentLength = resultMessage.length;
this.testCaseStore = Ext.create('Rally.data.custom.Store', {
data:resultMessage
});
this.pendingProjects = resultMessage.length
console.log("this testcase store:",resultMessage);
_.each(resultMessage, function (data) {
var objID = data.ObjectID;
var column1 = data.Permission;
console.log("this result message:",column1);
if(userObjID[1]==objID) {
console.log("obj id 1 is:",objID);
console.log("User Reference 2:",userObjID[1]);
if (data.Permission != 'Editor') {
deferred.resolve(this.testCaseStore);
}else{
this.testCaseStore = Ext.create('Rally.data.custom.Store', {
data:blankdata
});
deferred.resolve(this.testCaseStore);
}
}
},this)
},
failure: function (response) {
deferred.reject(response.status);
Ext.Msg.alert('Status', 'Request Failed.');
}
});
return deferred;
},
The main issue comes from your Ajax request which is using
async:false
This is blocking the javascript (unique) thread.
Consider removing it if possible. Note that there is no guarantee XMLHttpRequest synchronous requests will be supported in the future.
You'll also have to add in your success and failure callbacks:
that.setLoading(false);

uploading profile pic in hapijs 17.0

I am using hapijs version 17.0.1. I am trying to upload an image using ajax request on a hapijs route. Here is my AJAX code to upload profile pic:
var image_file_input = document.getElementById("user_profile_upload");
image_file_input.onchange = function () {
if(this.files != undefined)
{
if(this.files[0] != undefined)
{
var formData = tests.formdata ? new FormData() : null;
if (tests.formdata)
{
//alert(file)
formData.append('image_file', this.files[0]);
formData.append('userId', user_id);
formData.append('memberId', member_id);
}
$.ajax({
url: "/v1/User/uploadUserPic",
data: formData,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
contentType: "multipart/form-data",
success: function(data){
console.log(data);
var errMsg = null;
var resData = null;
if(data.statusCode == 200)
{
resData = data.result;
}
else
{
alert(data.message)
}
},
error: function(error){
alert(error);
}
});
}
}
}
And here is my Hapijs route Code:
var uploadUserPic = {
method: 'POST',
path: '/v1/Module/uploadUserPic',
config: {
description: 'Update Image For User',
tags: ['api', 'User'],
auth: 'session',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
userId : Joi.string().regex(/^[a-f\d]{24}$/i).required(),
memberId: Joi.string().required(),
image_file: Joi.object().required(),
},
failAction: FailCallBack
}
},
handler: function (request, reply) {
var resultData = null;
var error = null;
return new Promise(function (resolve) {
var multiparty = require('multiparty');
var fs = require('fs');
var form = new multiparty.Form();
form.parse(request.payload, function (err, fields, files) {
if(err)
{
error = err;
resolve();
}
else
{
var mkdirp = require('mkdirp');
var img_dir = "./files/users/";
mkdirp(img_dir, function (err) {
if (err)
{
error = err;
console.error(err);
resolve();
}
else
{
var oldpath = files.image_file.path;
var newpath = "./files/users/"+requestPayload.userId+".png";
fs.rename(oldpath, newpath, function (err) {
if(err)
{
error = err;
}
resolve();
});
}
});
}
});
}).then(function (err, result) {
if(err) return sendError(err);
if(error) return sendError(error)
return {
"statusCode": 200,
"success": true
};
});
}
}
The above code gives me following error cannot read property 'content-length' of undefined on line form.parse(request.payload, function (err, fields, files) {});
Please let me know If I am doing something wrong. If I replace the url in ajax request with anohter url that I have written in php then it works perfectly. which means that something is wrong with my hapijs/nodejs code.
There's a good post on how to handle file uploads in Hapi.js (written in version 16) https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js
Since you are using payload.parse = true, I am not seeing a particular reason why you have to use multiparty. I have the following working code that would save files (of any type) uploaded from client into uploads directory on the server (Please do not use directly on production as no sanitation is done)
{
path: '/upload',
method: 'POST',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
files: Joi.array().single()
}
}
},
handler: function(request) {
const p = request.payload, files = p.files
if(files) {
console.log(`${files.length} files`)
files.forEach(async file => {
const filename= file.hapi.filename
console.log(`Saving ${filename} to ./uploads`)
const out = fs.createWriteStream(`./uploads/${filename}`)
await file.pipe(out)
})
}
return {result: 'ok'}
}
}
You can use the following curl command to test
curl http://localhost:8080/upload -F 'files=#/path/to/a/note.txt' -F 'files=#/path/to/test.png' -vvv
There are a few issues with your code. First in your $.ajax call, you have specified contentType twice, although it's not a syntax error but it's careless to code like that. Second the function's signature inside your .then() block is incorrect. You are mixing the idea of Promise and callback. I don't think the following line will be triggered
if(err) return sendError(err);
One last trivial thing, you said you are using Hapi 17 but based on the handler function's signature
handler: function (request, reply) {
...
Seems you are not totally onboard with Hapi17 as the new signature is
handler: function (request, h) {
And it's not just the rename of reply to h.

BreezeJs - How to make simultaneous AJAX calls?

I wrote two Ajax calls that request data from stored procedures (in SQL Server), sp_ahtreatmentselect and sp_inventoryselect. Here is how the functions look like in the Breeze controller.
[HttpPost]
[ActionName("getinventories")]
public object GetInventories(HttpRequestMessage request)
{
var data = request.Content.ReadAsFormDataAsync().Result;
var opId = data["operationid"];
string query = "sp_inventoryselect #operationId";
SqlParameter operationId = new SqlParameter("#operationId", opId);
return UnitOfWork.Context().ExecuteStoreQuery<GetInventories>(query, operationId);
}
[HttpPost]
[ActionName("gettreatments")]
public object GetTreatments(HttpRequestMessage request)
{
var data = request.Content.ReadAsFormDataAsync().Result;
var opId = data["operationid"];
string query = "sp_ahtreatmentselect #operationId";
SqlParameter operationId = new SqlParameter("#operationId", opId);
return UnitOfWork.Context().ExecuteStoreQuery<GetTreatments>(query, operationId);
}
Now, on client-side, the Ajax calls look like this:
var ajaxImpl = breeze.config.getAdapterInstance('ajax');
function treatments(id) {
return ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/gettreatments',
data: { operationid: id },
success: function(data) {
console.log('Success!');
},
error: function(error) {
console.log('Error!');
}
});
}
function inventories(id) {
return ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/getinventories',
data: { operationid: id },
success: function (data) {
console.log('Success!');
},
error: function(error) {
console.log('Error!');
}
});
}
return inventories(id).then(treatments(id))
.then(function() {
// Do something
})
.fail(function(error) {
// Display error
});
Both Ajax calls work fine, BUT problem is, // Do something is run BEFORE inventories(id) and treatments(id) are run. I would like it to work the other way around instead. I also tried $.when(inventories(id), treatments(id)).then(...) and $.when(ajaxImpl.ajax(...), ajaxImpl.ajax(...)).then(...), but same problem occurs. How do I solve this?
Thanks in advance.
I realized that the issue lay on synchronous calls, not Breeze. I used queue.js to make Ajax calls and // Do something and the calls are completed. Here is how.
var ajaxImpl = breeze.config.getAdapterInstance('ajax'),
serviceName = 'some/service',
id = 1; // Could be any number
return queue()
.defer(function(callback) {
ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/getinventories',
data: { operationid: id },
success: function (data) {
console.log('Success!');
callback(null, data);
});
})
.defer(function(callback) {
ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/gettreatments',
data: { operationid: id },
success: function (data) {
console.log('Success!');
callback(null, data);
});
})
.awaitAll(function(error, results) {
if (error) {
console.log('Error! ' + error);
} else {
// Do something, given that:
// results[0] are inventories, and
// results[1] are treatments
}
});
I don't know what this has to do with Breeze. Did you get your ajaxImpl from a Breeze ajax adapter? I've looked at both ajax adapters shipped with Breeze and neither of them returns anything from a call to the ajax method!
Accordingly, you're expression should have died immediately with a reference error when it got to the .then in return inventories(id).then(.... I don't see how it could have gotten to \\ do something at all.
Something isn't right with the way you've posed this question.
Once you get beyond this I still don't see how this involves Breeze. Breeze won't do anything with the results of your service requests.
I'm completely flummoxed by your question.

Removing a subdoc using AJAX & Mongoose

How do you properly delete a subdoc (a task in this case) with AJAX in Mongoose?
Everything seems to be working up until the ajax in the file that's loaded into the page. Or could the problem be in the controller? I have read that you can't perform a .remove on a child element and I'm unclear on how to handle a delete.
Here is the schema:
//new user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
// Task schema
var taskSchema = mongoose.Schema({
clientEasyTask : { type: String },
clientHardTask : { type: String },
clientStupidTask : { type: String }
});
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
password: String,
task : [taskSchema]
});
module.exports = mongoose.model('Task', taskSchema);
module.exports = mongoose.model('User', userSchema);
The JS loaded into the page:
// Delete
$(document).ready(function() {
console.log('called del function');
var $alert = $('.alert');
$alert.hide();
$alert.on('error', function(event, data){
$alert.html(data)
$alert.addClass('alert-danger');
$alert.show();
});
$alert.on('success', function(event, data) {
$alert.html(data);
$alert.addClass('alert-info');
$alert.show();
})
$('.task-delete').click(function(event) {
console.log('click event occurred');
$target = $(event.target)
$.ajax({
type: 'DELETE',
url: apiDeleteTask + $target.attr('data-task-id'),
success: function(response) {
$target.parent.children.id(id).remove();
$alert.trigger('success', 'Task was removed.');
},
error: function(error) {
$alert.trigger('error', error);
}
})
});
})
Routes, which matches the working update route:
var tasks = require('./controllers/tasks-controller'),
var User = require('./models/user');
var Task = require('./models/user');
module.exports = function (app, passport) {
// Delete Task
app.delete('/api/tasks/:id', tasks.del);
};
And the tasks-controller.js
var User = require('../models/user');
var Task = require('../models/user');
exports.del = function(req, res, next) {
return User.update({ 'task._id': req.params.id }, { $set: { 'task.$.clientEasyTask': req.body.clientEasyTask }},
(function(err, user) {
if(!user) {
res.statusCode = 404;
return res.send({ error: 'Not phound' });
}
if(!err) {
console.log("Updated Existing Task with ID: " + req.params.id + " to read: " + req.body.clientEasyTask ),
res.redirect('/dashboard');
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
})
);
};
And last but not least I'm getting this error, that gives the task_id string & line 0:
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (54c55ac0443873db1eb8c00c, line 0)
In order to remove an entire field from the child array (tasks) the solution is to use $unset. I was wanting to use $set to update the field with a null value, but this is exactly what $unset does.
Here is the line in question that now works:
return User.update({ 'task._id': req.params.id }, { $unset: { 'task.$.clientEasyTask': req.body.clientEasyTask }},
Read more about field operators here: http://docs.mongodb.org/manual/reference/operator/update-field/
$pull would work if you want to remove the array elements without leaving behind a null value, but you must have a specific, matching query. Read about $pull and other array update options here:
http://docs.mongodb.org/manual/reference/operator/update-array/
Also, if you are struggling with a problem I can't stress how important it is to read the documentation. I can guarantee you that everyone on here that is answering problems is doing this, or has learned from someone who does.
Do the work. You'll figure it out. Don't give up.

Node.js modified AJAX insertion to MongoDB

I am succesfully posting an AJAX insert in my MondoDB database.
The user is supposed to fill in 3 fields,
Full Name
Email
Phone
What I would like to do is:
generate a random number in server-side and save it as a 4th field in my MongoDB.
Also I would like to post it as a response back to the user.
Here is my users.js file (server-side)
* POST to adduser.
*/
router.post('/adduser', function(req, res) {
var db = req.db;
var codeResponse = generateCode();
db.collection('userlist').insert(req.body, function(err, result){
res.send(
(err === null) ? { msg: '',code: codeResponse } : { msg: err }
);
});
});
function generateCode(){
var code = Math.random() *1000000;
code = Math.floor(code);
return code;
}
And this is my AJAX call(client-side)
var newUser = {
'id2': id2,
'fullname': $('#addUser fieldset input#inputUserFullname').val(),
'email': $('#addUser fieldset input#inputUserEmail').val(),
'phone': $('#addUser fieldset input#inputUserPhone').val(),
}
$.ajax({
type: 'POST',
data: newUser,
url: '/users/adduser',
dataType: 'JSON'
}).done(function( response ) {
// Check for successful (blank) response
if (response.msg === '') {
console.log(response);
}
else {
alert('Error: ' + response.msg);
}
});
Easy enough, add it to your object before insert and post back the object:
router.post('/adduser', function(req, res) {
var db = req.db;
var document = req.body;
var codeResponse = generateCode();
document.code = codeResponse;
db.collection('userlist').insert(document, function(err, result){
if (err) //do something
return;
else
res.send(document);
});
});

Resources