Sending additional data with programatically created Dropzone using the sending event - node.js

I have the following (simplified for example) angular directive which creates a dropzone
directives.directive('dropzone', ['dropZoneFactory', function(dropZoneFactory){
'use strict';
return {
restrict: 'C',
link : function(scope, element, attrs){
new Dropzone('#'+attrs.id, {url: attrs.url});
var myDropZone = Dropzone.forElement('#'+attrs.id);
myDropZone.on('sending', function(file, xhr, formData){
//this gets triggered
console.log('sending');
formData.userName='bob';
});
}
}
}]);
As you can see the the sending event handler I'm trying to send the username ("bob") along with the uploaded file. However, I can't seem to retrieve it in my route middleware as req.params comes back as an empty array (I've also tried req.body).
My node route
{
path: '/uploads',
httpMethod: 'POST',
middleware: [express.bodyParser({ keepExtensions: true, uploadDir: 'uploads'}),function(request,response){
// comes back as []
console.log(request.params);
//this sees the files fine
console.log(request.files);
response.end("upload complete");
}]
}
Here is what the docs say on the sending event
Called just before each file is sent. Gets the xhr object and the formData objects as second and third parameters, so you can modify them (for example to add a CSRF token) or add additional data.
EDIT
I dropped the programmatic approach for now. I have two forms submitting to the same endpoint, a regular one with just post and a dropzone one. Both work, so I don't think it's an issue with the endpoint rather with how I handle the 'sending' event.
//Receives the POST var just fine
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz")
input(type="hidden", name="additionaldata", value="1")
input(type="submit")
//With this one I can get the POST var
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz2", class="dropzone")
input(type="hidden", name="additionaldata", value="1")

OK, I've actually figured it out, thanks to Using Dropzone.js to upload after new user creation, send headers
The sending event:
myDropZone.on('sending', function(file, xhr, formData){
formData.append('userName', 'bob');
});
As opposed to formData.userName = 'bob' which doesn't work for some reason.

I would like to add to NicolasMoise's answer.
As a beginner in webdev I got stuck on how to obtain an instance of Dropzone. I wanted to retrieve an instance of Dropzone that had been generated by the autodiscovery feature. But it turns out that the easiest way to do this is to manually add a Dropzone instance after first telling Dropzone not to auto-discover.
<input id="pathInput"/>
<div id="uploadForm" class="dropzone"/>
<script>
$(document).ready(function(){
Dropzone.autoDiscover = false;
var dZone = new Dropzone("div#uploadForm", {url: "/api/uploads"});
dZone.on("sending", function(file, xhr, data){
data.append("uploadFolder", $("#pathInput")[0].value);
});
});
</script>
Serverside the data will be in request.body.uploadFolder

Nicolas answer is one possible solution to the problem. It is especially useful if you need to alter the file object prior to sending.
An alternative is to use the params option:
var myDropzone = new Dropzone("div#myId",
{ url: "/file/post", params: { 'param_1': 1 }});
cf. the documention

For those that are using thatisuday/ng-dropzone the callback methods are done as such:
<ng-dropzone class="dropzone" options="dzOptions" callbacks="dzCallbacks" methods="dzMethods"></ng-dropzone>
In a controller:
$scope.dzCallbacks = {
sending: function(file, xhr, form) {
console.log('custom sending', arguments);
form.append('a', 'b');
}
};

Related

Error: Can't set headers after they are sent - Ajax, Node, Express

I'm trying to using Airtable, node.js, express.js and jquery to create a simple user authentication functionality but I'm fairly new at this and I'm running into a problem I can't seem to fix and the articles I've read I can't seem to grasp or adapt to my particular situation.
I have this Ajax call in my html doc:
$("#checkUser").submit(function(e) {
var studentID = $('input[name="student"]').val()
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(), // serializes the form's elements.
success: function(data) {
$(window).attr("location", window.location.href + 'Dashboard?student=' + studentID);
},
error: function(data){
console.log("User not found. Try again");
}
});
});
This call sends the inputted username and data to the server which then processes it in the following way:
app.post('/checkUser', urlencodedParser, function(request,response){
var user = JSON.stringify(request.body);
user = JSON.parse(user);
base('RegisteredStudents').select({
filterByFormula: '{UserID} = ' + user.student,
view: "Grid view"
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
response.sendStatus(200);
});
fetchNextPage();
}, function done(error) {
response.sendStatus(404);
});
});
If the user exists in the database of Airtable, it should send '200' which the Ajax then reacts by redirecting accordingly to the user's profile. Otherwise, if the user does not exist, the server should respond with '404', which the Ajax call should react to by printing a statement in the console. While it does do these two things well, the server breaks down when, after a student puts in the wrong user ID and the Ajax prints the statement, the student tries to put once more a userID. I get the " Can't set headers after they are sent. " message. Please, how can I solve this?
Thank you!
You have two response.send..., you can only send data once. Either make sure only one runs with some conditional or add return before all response.send... so if any of them runs, the program will return and the other response.send.. will not run.

Node.JS run routes synchronously

In this example, I have two routes - the first is a Get route and the second is a Post route. I want the information gathered in the get route to be included in the post route. I tried using .then and some basic boolean if logic but I cannot get these routes to run synchronously.
leadFormObj = {};
$.get("/getID/"+leadFormObj.parentEmail, function(event){
console.log("getting an ID");
console.log(event[0].id);
leadFormObj.parentID = event[0].id;
});
console.log(leadFormObj);
$.post("/addChild", leadFormObj, function(data) {
console.log(leadFormObj);
console.log("sent");
});
In the example above, I have a standard object (i've shown it blank in this example). The first get route will run and pass in a new key value pair to the object. I then want to pass this updated object to the post route but I'm not sure how to do this.
Would I use nested routes to do this?
Thanks!
Why don't you put the post request inside the callback of the get request
$.get("/getID/"+leadFormObj.parentEmail, function(data){
$.post("/addChild", {parentID: data[0].id}, function(data) {
console.log("sent");
});
});

Send variables to all routes in exressjs

Using nodejs/expressjs to build the APIs for my web app, I want to send some variables to all APIs, such as site title and description and so on.
I stumbled upon the old solution using dynamicHelper() which is no longer in use. What is the new approach to do so?
Easiest thing is to just put in some middleware that attaches it to the response object as locals (those will show up in your views automatically). Something like:
app.use(function(req,res,next) {
res.locals = {
title : 'your title',
description : 'your description'
};
return next();
});
** EDIT to account for what the API endpoints have to do
Since each endpoint is likely responsible for its own object, you would also do something like:
app.get('/whatever', function(req,res){
var json = {};
// do whatever to build your json
json.metadata = res.locals; // or whatever the common stuff is
res.send(json);
}
This keeps all your 'common' stuff in one part of the json response.
Since you mention you are not using any view engine in expressjs, I am assuming you are just relying on angularJS to do the client side redering. You can pass those server side data to the http header, and then read them from the client side. To do that, in your router, you can do this,
app.use(function(req,res,next) {
res.set({
'title': 'my title',
'description': '123'
});
next();
});
Then in your angularJS app, you can read them from the http header.
You should try interceptors in your front end side(angular js) to send multiple variable with each request api.
In following code i am sending title and description in headers.
module.factory('varInfoInterceptors', function($q) {
var sendInfoInjector = {
request: function(config) {
config.headers['x-headers-title'] = 'Test title';
config.headers['x-headers-desc'] = 'This is test site';
return config;
}
};
return sendInfoInjector;
});
module.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('varInfoInterceptors');
}]);
You can get these values in server side(nodejs/expressjs) by just calling req.headers express routes
Thanks,
Dinesh

how to publish a page using node.js

I have just begun to learn node.js. Over the last two days, I've been working on a project that accepts userinput and publishes a ICS file. I have all of that working. Now consider when I have to show this data. I get a router.get to see if I am at the /cal page and..
router.get('/cal', function(req, res, next)
{
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
//console.log(ical.toString());
});
});
res.send(ical.toString());
// res.render('index', {
// title: 'Cal View'
// })
})
So when /cal is requested, it loops through my db and creates an ICS calendar ical. If I do console.log(ical.toString) within the loop, it gives me a properly formatted calendar following the protocol.
However, I'd like to END the response with this. At the end I do a res.send just to see what gets published on the page. This is what gets published
BEGIN:VCALENDAR VERSION:2.0
PRODID:calendar//EN
END:VCALENDAR
Now the reason is pretty obvious. Its the nature of node.js. The response gets sent to the browser before the callback function finishes adding each individual VEVENT to the calendar object.
I have two related questions:
1) Whats the proper way to "wait" till the callback is done.
2) How
do I use res to send out a .ics dynamic link with
ical.toString() as the content. Do I need to create a new view for
this ?
edit: I guess for number 2 I'd have to set the HTTP headers like so
//set correct content-type-header
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
but how do I do this when using views.
Simply send the response, once you got the neccessary data! You are not required to end or send directly in your route but can do it in a nested callback as well:
router.get('/cal', function(req, res, next) {
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
});
res.type('ics');
res.send(ical.toString());
});
});
I also included sending the proper Content-Type by using res.type.
Also: Don't forget to add proper error handling. You can for example use res.sendStatus(500) if an error occured while retrieving the documents.

Express.js and request: Headers unexpectedly reset themselves

I'm using express with request to communicate with an API; a few controller files use exports so I can keep the route files pretty.
module.exports = function(app){
var moment = require('moment'),
request = require('request'),
auth = require('../auth/auth.js')(app),
apiUrl = process.env.API_URL;
return {
addProduct: function addProduct(req,res) ...
request.defaults are set in the auth file, login calls a function there to set headers to authenticate the user throughout the app.
auth.setToken = function(token, type, access) {
auth.request = request.defaults({
headers: {
'x-access-token': token,
'x-access-type': type,
'x-access-level': access,
}
});
};
And then use request with some mods.
auth.request.get(apiPath, function(err, apiRes) {
res.render(renderView, {
items: items,
});
});
Everything works great from the 'user' file, but I have a couple of other controller files I just added in, and for some reason the headers get reset (so my authentication gets lost) in every function in those files. I can literally copy and paste the 'user' file into the others and have the same problem, or I can put what I want in the other files back in the 'user' file and it works fine again. I can't figure out what I've done wrong but it's going to be very messy if I just have one file... any ideas?
Figured it out- it was an issue of variables not getting passed around among controller files the way I assumed they were, so I created a new function to copy the token from session back into the headers and all is resolved.

Resources