Issue with rendering page after performing delete function - javascript

exports.deleteItem = function (req, res)
{
var query = req.params.id;
var cart = req.session.items;
var index;
_.each(cart,function (ticket)
{
if(ticket.id === query)
{
index = cart.indexOf(ticket);
cart.splice(index,1);
}
return res.redirect(303, '/cart');
});
};
I am using this function in my routes to perform a delete operation. It works for the first few items on my list, then suddenly stops and gives me the error "Can't set headers after they are sent."
$(document).ready(function() {
$('.delete-from-cart').click(function(event)
{
$target = $(event.target);
$.ajax({
type: 'DELETE',
url: '/cart/remove/' + $target.attr('data-item-id'),
data: {
_csrf: $target.attr('data-csrf')
},
success: function(response)
{
$target.parent().parent().remove();
Materialize.toast('Ticket Removed', 4000);
window.location.href = '/cart';
},
error: function(error)
{
Materialize.toast('Error', 4000);
console.log(error);
}
});
});
});

A single request was made, which means a single response should occur.
When you execute the _.each function I'm guessing that it works if there is one item but fails when there are multiple? This is because you are trying to send multiple responses when only one is permitted.
A good explanation for this can be found here: Error: Can't set headers after they are sent to the client

Related

Parse Server Cloud Code background job query and update objects

I have a Parse Server app hosted on back4app and I am running a Background Job that runs every minute and queries the Letters class, where the column deliveryDate is less or equal to the current date, here's my main.js file:
// DELIVER A LETTER
Parse.Cloud.job("deliverLetter", function (request, status) {
var now = new Date();
// var nowTime = date.getTime();
var Letters = Parse.Object.extend("Letters");
var query = new Parse.Query(Letters);
query.lessThanOrEqualTo("deliveryDate", now);
query.find().then (function (objects) {
objects.forEach(function (obj) {
obj.set("isDelivered", true);
Parse.Cloud.useMasterKey();
obj.save(null, { useMasterKey: true } ).then(function(obj) {
response.success(obj);
}, function(error) {
response.error(error)
});
});
});
So, for instance, I save a row in the Letters class where deliveryDate is set to yesterday, in order for me to test this Cloud Code function. There's another column called isDelivered and it's set to False. So, my function above should set isDelivered into True and update my Letters's object.
But it doesn't work, so I don't know what I'm doing wrong.
Edit
Thanks to danh, I've fixed my code as it follows:
var Letters = Parse.Object.extend("Letters");
var query = new Parse.Query(Letters);
query.lessThanOrEqualTo("deliveryDate", now);
query.equalTo("isDelivered", false);
query.find().then (function (objects) {
let savePromises = objects.map(function (obj) {
obj.set("isDelivered", true);
return obj.save(null, { useMasterKey: true } );
});
Promise.all(savePromises).then(function(obj) {
response.success(obj);
}, function(error) {
response.error(error)
});
});
I would need to call another function from my main.js file which sends a push notifications and needs some parameters. I usually call it from my app, how would I call it from within that above function?
Parse.Cloud.define("pushiOS", function(request, response) {
var user = request.user;
var params = request.params;
var userObjectID = params.userObjectID
var data = params.data
var recipientUser = new Parse.User();
recipientUser.id = userObjectID;
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("userID", userObjectID);
Parse.Push.send({
where: pushQuery,
data: data
}, { success: function() {
console.log("#### PUSH SENT!");
}, error: function(error) {
console.log("#### PUSH ERROR: " + error.message);
}, useMasterKey: true});
response.success('success');
});
Maybe into Promise.all()?
Promise.all(savePromises).then(function(obj) {
response.success(obj);
Parse.Cloud.define("pushiOS"...
}, function(error) {
response.error(error)
});
Any save() that's in progress or not yet started when response.success() is called will be terminated (or won't get a chance to start). To fix, collect promises for all of the saves, and run them together with Promise.all(), which resolves only after all of the promises passed to it have resolved.
query.find().then (function (objects) {
// Parse.Cloud.useMasterKey(); don't need this
let savePromises = objects.map(function (obj) {
obj.set("isDelivered", true);
return obj.save(null, { useMasterKey: true } );
});
Promise.all(savePromises).then(function(obj) {
response.success(obj);
}, function(error) {
response.error(error)
});
});
Also, note, incidentally, that query.lessThanOrEqualTo("deliveryDate", now); will get all of the objects with deliveryDates before now, including the ones you processed previously. That result will get monotonically longer over time, eventually exceeding the 1 minute between runs, or blowing some other system resource.
Maybe you really want...
query.lessThanOrEqualTo("deliveryDate", now);
query.equalTo("isDelivered", false);
EDIT
The second question can be handled by factoring a promise-returning push function like this...
function pushDataToUser(userID, data)
var recipientUser = new Parse.User();
recipientUser.id = userID;
let pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("userID", userID);
return Parse.Push.send({ where:pushQuery, data:data });
}
That can be called from cloud code like this...
Parse.Cloud.define("pushiOS", function(request, response) {
let params = request.params;
pushDataToUser(params.userObjectID, params.data).then(function() {
console.log("#### PUSH SENT!");
response.success('success');
}, function(error) {
console.log("#### PUSH ERROR! " + JSON.stringify(error));
response.error(error);
});
}
...and the new promise-returning function can be added to any other promise chain like this...
// function doSomething() returns a promise (like from a find() or a save()
return doSomething().then(function() {
// initialize someId and someData
return pushDataToUser(someId, someData)
});

Javascript Object Undefined - Jquery + Node JS

I'm facing this problem and I'm a newbie with Javascript and NodeJS, below I have this code at my Route on /update/:id
controller.save = (req, res) => {
const data = req.body
const name = req.body.name
const cpf = req.body.cpf
req.getConnection((err, connection) => {
const query = connection.query(
`INSERT INTO clientes(Nome, CPF)
VALUES('${name}','${cpf}')`,
data,
(err, clientes) => {
res.json(clientes)
}
)
})
}
and I have a form that have a Button called "Update", when I click , the AJAX made this .
$(document).on("click", ".update", function() {
var user_id = $(this).attr("id")
$.ajax({
url: "/update/" + user_id,
method: "GET",
dataType: "json",
success: function(to) {
alert(to)
}
})
})
I'm receive a alert [Object Object], when I go to my Network response I have this:
[{"ID":5,"Nome":"tobiaas","CPF":"107"}]
when I change alert to alert(to.Nome), I receive a alert Undefined
I don't wanna use .map, because i thing that this is a simple way to made work .
You are receiving an array as the response, Array.Nome does not exists, your output is normal.
You need to get the item with response[0] or response.pop() to access it:
success: function(response) {
const to = response[0]
alert(to.Nome)
}
For more info on javascript array access, see W3School

async.js Ordering of functions

So I'm having trouble getting one javascript function to finish before the next one starting. I've spent quite a lot of time trying to use callback methods described on other stackoverflow posts. I could get simple examples that used timeouts to work but couldn't get it to work with my API request. I stumbled upon async.js and thought that perhaps using async.series would be a good idea to get my two functions to perform one after another. So I tried this approach, however I still seem to be having the problem where the first function takes a bit longer to execute (which is fine) but the execution process moves past this function instead of waiting for it to end. I feel I have a misconception of some sort since I have tried several methods but to no avail.
What is strange is, is that that when running server.js, it goes into the first function but then it leaves the async.series() function even before the request is finished. When I print inside of tokenReq(), I can see that the request was successful as a token code is returned successfully however this happens to late as execution has moved on. The output is shown below.
server.js:
var access_code;
async.series([
function() {
access_code = queries.data.tokenReq(code);
console.log("Finished inside function 1");
},
function() {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
}
],
function(err, access_code) {
});
console.log("Outside");
queries.js:
tokenReq: function(code) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return access_token;
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return "error";
}
});
},
Output:
Finished inside function 1
Outside
INSIDE FUNCTION REQUEST, Token: 8Swhd.......
You missed a major point here. Since node.js is asynchronous there should not be a way to know when a function completes its execution. That is why we specify callbacks so that the invoking function knows whom to call when it finishes its execution. Once you have functions with callbacks, you can enforce series/parallel/waterfall behavior with async module.
tokenReq: function(code, cb) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return cb(null, access_token);
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return cb(new Error("whatever"));
}
});
},
Now, you can use the callback inside server.js
var access_code;
async.series([
function(cb) {
return queries.data.tokenReq(code, cb);
},
function(access_code, cb) {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
// do whatever you want after this
return cb();
}
],
function(err, access_code) {
if (err) {
console.log(err);
}
// wrap your logic around a function and call the correspoding callback here
});

Node.JS Request Module Callback Not Firing

I'm running this code using the request module for node.js
var hsKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var hsForm = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var hsHost = "https://docs.google.com/"
var url = hsHost + "forms/d/" + hsForm + "/formResponse"
var form = {
"entry.129401737": pointsAvg,
"entry.2000749128": hiddenNeurons,
"submit": "Submit",
"formkey": hsKey
};
request.post({
url: url,
form: form
}, function (err, res, body) {
console.log("Sent data");
});
I have tried running the above code just using standard Node.JS libraries, to no avail. The callback function is never fired and the request doesn't go through. I don't know why.
I believe I've found the answer to my own problem. The issue seems to be that I'm not allocating any time in the Node.js event loop to allow the request to be executed.
Have a look at this question:
your code should look something like
var hsKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var hsForm = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var hsHost = "https://docs.google.com/"
var url = hsHost + "forms/d/" + hsForm + "/formResponse"
var form = {
"entry.129401737": pointsAvg,
"entry.2000749128": hiddenNeurons,
"submit": "Submit",
"formkey": hsKey
};
request.post({
url: url,
form: form
}, function (response) {
response.setEncoding('utf8');
response.on('data', function(chunk){
//do something with chunk
});
});
The data event should get fired on receiving a response.
So if you read the docs for the request module at npm
request
.get('http://google.com/img.png')
.on('response', function(response) {
console.log(response.statusCode) // 200
console.log(response.headers['content-type']) // 'image/png'
});
There is a response event that should get fired.
I ran into this as well. I ended up creating a separate js file containing only the request, without the describe and it methods, and running it with 'mocha mynewbarebonesreq.js'. suddenly I could see that there was an exception being thrown and swallowed by mocha (with the standard reporter, spec).
I finally installed and enabled mocha_reporter which shows the exceptions
now it looks like this:
describe('CMSLogin', function () {
it('should log in as user ' + JSON.stringify(USER_PASS), function (done) {
request({
url: "http://cms.lund.multiq.com:3000/api/CMSUsers/login",
method: "POST",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
json: false,
body: JSON.stringify(USER_PASS)
}, (err, res, body) => {
var parsedBody = JSON.parse(body);
this.token = parsedBody.id;
console.log(this.token)
assert.equal(USER_PASS.userId, parsedBody.userId);
assert.doesNotThrow(() => Date.parse(parsedBody.created));
if (err) { done.fail(err); }
done();
});
});
}

Why am I getting this error in Node: Error: Can't set headers after they are sent

Every other time I load the page with an ajax call to most_voted I get the following error. Why is this happening?
Error
http.js:692
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
Controller
// Add Voted Attribute
add_voted_attribute = function(user, product, callback) {
Vote.find({ product: product._id, user: user._id }).exec(function(err, vote) {
if (vote.length) {
product = product.toObject();
product.voted = true;
if (callback) { callback(product) };
} else {
product = product.toObject();
product.voted = false;
if (callback) { callback(product) };
};
});
};
// Show Most Voted Products
exports.most_voted = function(req, res) {
Product.find().sort({votes: -1}).limit(10).populate('user', 'name username').exec(function(err, products) {
var products_with_voted_attribute = [];
products.forEach(function(p){
add_voted_attribute(req.user, p, function(product){
products_with_voted_attribute.push(product);
if (products_with_voted_attribute.length === 10) {
eventEmitter.emit('mostVoted', products_with_voted_attribute);
};
});
});
});
// Event Listener
eventEmitter.on('mostVoted', function(products) {
products.sort(function(obj1, obj2) {
return obj2.votes - obj1.votes;
});
res.jsonp(products);
});
};
You can only call res.jsonp once per request. Calling this multiple times causes headers to be sent multiple times (Content-Type).
The eventEmitter was firing multiple times. I'm not sure why this is, but I solved it by simply removing the event listener after it fires once. This fix required only one line of code:
// Event Listener
eventEmitter.on('mostVoted', function(products) {
eventEmitter.removeAllListeners('mostVoted'); // Line of code to remove listener

Categories

Resources