Skip to main content

UploadableMixin

Enables creating and modifying the resource behind a Backbone.Model. Simplifies requests with multi-part content, enables mocking by mockjax and supports pasing the JSON body as form-encoded parameter "body". Also introduces the prepare method to "massage" request body similarly to the parse method for the response body.

How to apply the mixin to a model

var MyModel = Backbone.Model.extend({
constructor: function MyModel(attributes, options) {
Backbone.Model.prototype.constructor.apply(this, arguments);
this.makeConnectable(options)
.makeUploadable(options);
},

url: function () {
return Url.combine(this.connector.connection.url, 'myresource')
}
});

ConnectableMixin.mixin(MyModel.prototype);
UploadableMixin.mixin(MyModel.prototype);

This mixin us usually combined together with the ConnectableMixin or with another cumulated mixin which includes it.

How to use the mixin

It just works whenever you call the save method of the model.

makeUploadable(options) : this

Must be called in the constructor to initialize the mixin functionality. Expects the Backbone.Model or Backbone.Collection constructor options passed in.

prepare(data, options) : object

Can be overridden to "massage" the data, which are going to be sent to the server in the request body. If not overridden, the unchanged input of the save method, or this.attributes, will be sent to the server as-is. The prepare method converts model attributes to the request body object, so that the server accepts it, like the parse method converts the response body object to model attributes, so that the client understands them.

// For example, the server sends and receives resource attributes wrapped
// in an object with a `data` property.
var MyModel = Backbone.Model.extend({
constructor: function MyModel(attributes, options) {
Backbone.Model.prototype.constructor.apply(this, arguments);
this.makeUploadable(options);
},

prepare: function (data, options) {
return {data: data};
},

parse: function (response, options) {
return response.data;
}
});

UploadableMixin.mixin(MyModel.prototype);

Calling the prepare method can be prevented by setting the prepare option to false, similarly to preventing the parse method from being called by setting the parse option to false.

See Also

ConnectableMixin, ResourceMixin

Motivation

PATCH Semantics Support

The CS REST API implements neither PUT nor PATCH semantics correctly, which makes usage of high-level JavaScript frameworks like Backbone impossible. The resource modification request has to be built in a custom way, so that it follows the PATCH semantics, but uses the PUT verb. This mixin allows usage of the Backbone in the usual way - with patch-mode for modifications.

// Rename a concrete node
var connector = new Connector({
connection: {
url: '//server/instance/cs/api/v1',
supportPath: '/instancesupport'
}
}),
node = new NodeModel({
id: 12345
}, {
connector: connector
});
node.save({
// properties to change on the server and in the model
name: 'New name'
}, {
patch: true, // send only properties specified above;
// not everything from this.attributes
wait: true // set the properties to this.attributes
// only if and after the request succeeds
})
.done(function () {
console.log('New name:', node.get('name'));
})
.fail(function () {
console.log('Renaming the node failed.',
'Old name:', node.get('name'));
});

Another missing feature in the CS REST API is returning the created and modified properties from the POST and PUT responses. If you need the model complete after a modification, you need to fetch it again, wasting a server call:

// Rename a concrete node and refresh other properties like `modify_date`
node.save({
name: 'New name'
}, {
patch: true,
wait: true
})
.then(function () {
return node.fetch();
})
.done(function () {
console.log('New name:', node.get('name'));
})
.fail(function () {
console.log('Renaming the node failed.');
});

File Upload Support

If the newly created resource needs a raw file content, you can pass the fields of the file type via options and let the mixin build the right request payload and set ist content type.

// Upload a new document
var connector = new Connector({
connection: {
url: '//server/instance/cs/api/v1',
supportPath: '/instancesupport'
}
}),
node = new NodeModel({
type: 144
}, {
connector: connector
}),
file = ...; // a File or Blob object
node.save({
name: 'New document',
parent_id: 2000
}, {
files: {
file: file
}
})
.done(function () {
console.log('New document ID:', node.get('id'));
})
.fail(function (request) {
var error = new base.Error(request);
console.log('Uploading document failed:', error);
});

Because the CS REST API is not friendly to clients expecting RESTful APIs, you will need to fetch the newly created node to get all properties, wasting another server call:

// Upload a new document and get all common properties
var node = new NodeModel(undefined, {connector: connector}),
file = ...; // a File or Blob object
node.save({
type: 144,
name: 'New document',
parent_id: 2000
}, {
files: {
file: file
}
})
.then(function () {
return node.fetch();
})
.done(function () {
console.log('New document ID:', node.get('id'));
})
.fail(function (request) {
var error = new base.Error(request);
console.log('Uploading document failed:', error);
});