Reputation: 2160
Is there any way using Backbone.js and it's model architecture that I can send a formdata object to the server? The problem I'm running into is that everything Backbone sends is encoded as JSON so the formdata object is not properly sent (obviously).
I'm temporarily working around this by making a straight jQuery ajax request and including the formdata object as the data property, but this is less than ideal.
Upvotes: 15
Views: 7554
Reputation: 73
A simple one will be, hope this will help someone.
Create a object of Backbone Model:
var importModel = new ImportModel();
Call Save[POST] method of Backbone Model and Pass the FormData Object.
var objFormData = new FormData();
objFormData.append('userfile', files[0]);
importModel.save(objFormData, {
contentType: false,
data: objFormData,
processData: false,
success: function(data, status, xhr) { },
error: function(xhr, statusStr) { }
});
Upvotes: 0
Reputation: 132
Just use Backbone.emulateJSON = true;
: http://backbonejs.org/#Sync-emulateJSON
will cause the JSON to be serialized under a model parameter, and the request to be made with a application/x-www-form-urlencoded MIME type, as if from an HTML form.
Upvotes: 2
Reputation: 10132
None of the answers worked for me, below is simple and easy solution. By overriding sync
method and options.contentType
like this:
sync: function(method, model, options) {
options = _.extend({
contentType : 'application/x-www-form-urlencoded;charset=UTF-8'
}, options);
options.data = jQuery.param(model.toJSON());
return Backbone.sync.call(this, method, model, options);
}
Upvotes: 1
Reputation: 19974
I had the same issue. You can see above the way i solve it.
var $form = $("myFormSelector");
//==> GET MODEL FROM FORM
var model = new MyBackboneModel();
var myData = null;
var ajaxOptions = {};
// Check if it is a multipart request.
if ($form.hasFile()) {
myData = new FormData($form[0]);
ajaxOptions = {
type: "POST",
data: myData,
processData: false,
cache: false,
contentType: false
};
} else {
myData = $form.serializeObject();
}
// Save the model.
model.save(myData, $.extend({}, ajaxOptions, {
success: function(model, data, response) {
//==> INSERT SUCCESS
},
error: function(model, response) {
//==> INSERT ERROR
}
}));
The hasFile is a custom method that extends the JQuery functions.
$.fn.hasFile = function() {
if ($.type(this) === "undefined")
return false;
var hasFile = false;
$.each($(this).find(":file"), function(key, input) {
if ($(input).val().length > 0) {
hasFile = true;
}
});
return hasFile;
};
Upvotes: 2
Reputation: 26999
Here is a solution by overriding the sync
method, which I use to allow file uploads.
In this case I override the model's sync
method, but this can also be the Backbone.sync
method.
var FileModel = Backbone.Model.extend({
urlRoot: CMS_ADMIN_URL + '/config/files',
sync: function(method, model, options){
// Post data as FormData object on create to allow file upload
if(method == 'create'){
var formData = new FormData();
// Loop over model attributes and append to formData
_.each(model.attributes, function(value, key){
formData.append(key, value);
});
// Set processData and contentType to false so data is sent as FormData
_.defaults(options || (options = {}), {
data: formData,
processData: false,
contentType: false
});
}
return Backbone.sync.call(this, method, model, options);
}
});
EDIT:
To track upload progress, you can add a xhr
option to options:
...
_.defaults(options || (options = {}), {
data: formData,
processData: false,
contentType: false
xhr: function(){
// get the native XmlHttpRequest object
var xhr = $.ajaxSettings.xhr();
// set the onprogress event handler
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
console.log('%d%', (event.loaded / event.total) * 100);
// Trigger progress event on model for view updates
model.trigger('progress', (event.loaded / event.total) * 100);
}
};
// set the onload event handler
xhr.upload.onload = function(){
console.log('complete');
model.trigger('progress', 100);
};
// return the customized object
return xhr;
}
});
...
Upvotes: 19
Reputation: 6938
I had a similar requirement and here is what i did :
In View :
var HomeView = Backbone.View.extend({
el: "#template_loader",
initialize: function () {
console.log('Home View Initialized');
},
render: function () {
var inputData = {
cId: cId,
latitude: latitude,
longitude: longitude
};
var data = new FormData();
data.append('data', JSON.stringify(inputData));
that.model.save(data, {
data: data,
processData: false,
cache: false,
contentType: false,
success: function (model, result) {
alert("Success");
},
error: function () {
alert("Error");
}
});
}
});
Hope this helps.
Upvotes: 7
Reputation: 27628
Just to add an answer to this question, heres how I went about it without having to override the sync
:
In my view, I have somethign like:
$('#' + $(e.currentTarget).data('fileTarget')).trigger('click').unbind('change').bind('change', function(){
var form_data = new FormData();
form_data.append('file', $(this)[0].files[0]);
appManager.trigger('user:picture:change', form_data);
});
Which then triggers a function in a controller that does this:
var picture_entity = new appManager.Entities.ProfilePicture();
picture_entity.save(null, {
data: data,
contentType: false,
processData: false,
});
At that point, I'm overriding jQuery's data
with my FormData
object.
Upvotes: 7