After going through the backbone documentation, it is evident that to send a delete request for a model we need to set the urlRoot and id. Another technique which came to my mind was to implement a sync for my model.
since my server uses POST for delete, i use emulateHTTP = true.
How can i acheive the above task so that the request url for my model will be of the form
http://myWerService.com/myresource/deleteMyModel?modelName="abc"
So, how can i set my id as modelName
What is the difference between the below two url patterns
http://myWerService.com/myresource/deleteMyModel?modelName="abc"
http://myWerService.com/myresource/deleteMyModel/abc
The reason being, i saw every example use the second url pattern and i have no idea about the distinction between the two.
I have the url set for this model for create request, how can i use a different url (i want to use the above specified url) for sending a delete request
I am not sure if this is exactly what you are asking, but maybe this example will help:
var MyModel = Backbone.Model.extend({
ID: 0,
Name: '',
... other attributes for the model ...
},
initialize: function (id) {
this.ID = id;
},
idAttribute: 'Name', // set the id attribute as the name attribute
// you can have different urls for different operations
methodToURL: {
'read': 'GetModel',
'update': 'UpdateModel',
'delete': 'DeleteModel'
},
// overwrite the sync function
sync: function (method, model, options) {
options = options || {};
options.url = model.methodToURL[method.toLowerCase()];
switch (method) {
case 'read':
case 'update':
case 'delete':
options.url += '/' + this.id;
break;
}
Backbone.sync(method, model, options);
}
});
Related
I am using BackboneJS and have a model object to update.
self.model.save(
{
urlRoot: +self.model.get("personId")+"/deactivate"
},
{
success: function (model){
self.showMessage('Person deactivated', true, true);
self.model.fetch();
},
error : function(){
$("#save", self.el).button("reset");
}
});
Now my REST method looks like
#PUT
#Path("{id}/deactivate")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public CompanyVO deactivatePerson(#PathParam("id") Long id, PersonVO personVO) {
return modifyPerson(id, personVO);
}
My question is there is some issue how I am setting urlRoot to call the corresponding REST method.
Please let me know the correct way so that I can call the REST method and update the Person object.
The save method prototype is : model.save([attributes], [options])
The first parameter is attributes. Second one is options, such as url, success, error methods can be specified.
If you already have set all attributes of a model, then pass '[]' as first parameter to save i.e.
self.model.save([],{
urlRoot: +self.model.get("personId")+"/deactivate, //ensure protocol + domain name are added
success: function (model){
self.showMessage('Person deactivated', true, true);
self.model.fetch();
},
error : function(){
$("#save", self.el).button("reset");
}
});
I am trying to set up a variant fetch method on my backbone model that will fetch the current model for a given user. This is available from the API on /api/mealplans/owner/{username}/current.
I have written the following model. I commented out the URL Root, as the prototype fetch call was simply using the urlRoot and I wanted to see if that was overriding the url parameter I passed in portions somehow.
var mealPlan = Backbone.Model.extend({
name: 'Meal Plan',
//urlRoot: '/api/mealplans',
defaults: {},
fetchCurrent: function (username, attributes, options) {
attributes = attributes || {};
options = options || {};
if (options.url === undefined) {
options.url = "/api/mealplans/owner/" + username + "/current";
}
return Backbone.Model.prototype.fetch.call(this, attributes, options);
},
validate: function (attributes) {
// To be done
return null;
}
});
I've seen this done, in some variations in other places, such as at backbone.js use different urls for model save and fetch - In that case the code is slightly different (I started with that and broke it down to make it easier for me to read.)
The options object has the url parameter in it fine when I pass it to fetch, but then it seems to ignore it!
I was assuming the same parameters to fetch as to save - This is not the case.
The method signature for fetch ONLY takes 'options' and not 'attributes', hence the url parameter wasn't found.
The model code should look a bit more like this..
var mealPlan = Ministry.Model.extend({
name: 'Meal Plan',
urlRoot: '/api/mealplans',
defaults: {
},
fetchCurrent: function (username, options) {
options = options || {};
if (options.url === undefined) {
options.url = this.urlRoot + "/owner/" + username + "/current";
}
return Backbone.Model.prototype.fetch.call(this, options);
},
validate: function (attributes) {
// To be done
return null;
}
});
I think it is better to override url() method, like below:
var mealPlan = Ministry.Model.extend({
name: 'Meal Plan',
urlRoot: '/api/mealplans',
//--> this gets called each time fetch() builds its url
url: function () {
//call the parent url()
var url=Backbone.Model.prototype.url.call(this);
//here you can transform the url the way you need
url += "?code=xxxx";
return url;
}
...
besides, in your example above I think there is a mistake and you should replace fetchCurrent by fetch
To be quite honest I'm stuck trying to override Backbone's sync() method for a Model, I have the signature for the function in place, and it gets triggered correctly, but I don't know what to put in the function body in order for it to make a default call to DELETE but with extra arguments.
ie.
class Master.Models.Member extends Backbone.Model
urlRoot: '/api/members/'
sync: (method, model, options) ->
params = _.clone options
Backbone.sync method, model, params
I call it like this:
......
remove: ->
#model.destroy
collective_id: the_id
My intention there, is to pass the collective_id param you see there to the server. But even though it's inside the options hash for sync() and I clone it, It won't make it to the server!
How can I send that extra param to the server?
(As it is, the only thing that reaches the server is the Model's id)
Thanks in advance!
When you call .destroy(), .fetch() or .save() they all call Model.sync which only calls Backbone.sync. It's a proxy function. This is intended to provide a nice hook for modifying the AJAX behavior of a single model or any models that extend from that model.
Solution 1: Override the Global Backbone.sync to JSON.stringify and modify the contentType when you've specified data to be sent with the delete request.
Pros: You can call model.destroy() and optionally pass in an options parameter
Solution 2 - Override the Model.sync method.
Pros: The override only affects individual models. Isolated changes.
Cons: All models that wish to delete with data need to extend from the correct 'base model'
Solution 3 - Don't override anything and explicitly call model.sync with all of the stringify and contentType stuff.
Pros: Very isolated changes, won't affect any other models. Useful if you're integrating with a large codebase.
[Solution 1] - Global Override of Backbone.sync (this will affect all models)
javacript version
var oldBackboneSync = Backbone.sync;
Backbone.sync = function( method, model, options ) {
// delete request WITH data
if ( method === 'delete' && options.data ) {
options.data = JSON.stringify(options.data);
options.contentType = 'application/json';
} // else, business as usual.
return oldBackboneSync.apply(this, [method, model, options]);
}
Usage:
var model, SomeModel = Backbone.Model.extend({ /* urlRoot, initialize, etc... */});
model = new SomeModel();
model.destroy({
data: {
/* data payload to send with delete request */
}
});
[Solution 2] - Override Backbone.destroy on a base model and extend other models from that.
override
// Create your own 'enhanced' model
Backbone.EnhancedModel = Backbone.Model.extend({
destroy: function( options ) {
if ( options.data ) {
// properly formats data for back-end to parse
options.data = JSON.stringify(options.data);
}
// transform all delete requests to application/json
options.contentType = 'application/json';
Backbone.Model.prototype.destroy.call(this, options);
}
});
usage
var model, SomeModel = Backbone.EnhancedModel.extend({ /* urlRoot, initialize, etc... */})
model = new SomeModel();
SomeModel.destroy({
data: {
/* additional data payload */
}
});
[Solution 3] - Call .destroy() with correct parameters.
If sending data with your destroy requests is an isolated thing, then this is an adequate solution.
When calling model.destroy() pass in the data and contentType options like so:
javascript version/usage
var additionalData = { collective_id: 14 };
model.destroy({
data: JSON.stringify(additionalData),
contentType: 'application/json'
});
The "Problem" (with Backbone, not solutions):
Backbone.js makes the assumption (view source) that delete requests do not have a data payload.
// delete methods are excluded from having their data processed and contentType altered.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.contentType = 'application/json';
params.data = JSON.stringify(options.attrs || model.toJSON(options));
}
In their assumed RESTful API call, the only data required is the ID which should be appended to a urlRoot property.
var BookModel = Backbone.Model.extend({
urlRoot: 'api/book'
});
var book1 = new BookModel({ id: 1 });
book1.destroy()
The delete request would be sent like
DELETE => api/book/1
contentType: Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Params need to be sent in options.data, so try:
coffeescript
remove: () ->
#model.destroy
data: JSON.stringify
collective_id: the_id,
contentType: 'application/json'
javascript
remove: function() {
return this.model.destroy({
data: JSON.stringify({
collective_id: the_id
}),
contentType: 'application/json'
});
}
I would like to change the URL generated when my entity calls destroy. Instead of writing an HTTP DELETE to /{Action}/{EntityID}, I would like to send /{Action}/{EntityID}/{SecondEntityID}.
item.destroy({
data: $.param({
playlistId: playlistId
}),
processData: true,
success: callback,
error: function (error) {
console.error(error);
}
});
I thought that something like this might work, but it doesn't seem to append on any additional parameters. Do I have to implement my own sync method in its entirety if I want to extend just destroys' URL?
You can override is through passing in a .url property in options when you call destroy. Since I assume you'd want to do this for every single call, you can do this:
var MyModel = Backbone.Model.extend({
destroy: function(options) {
// Override URL
options || (options = {});
// You can put whatever you need here,
options.url = 'http://www.awesome.com/destroy/' + this.get('id') + '/' + this.get('secondaryId');
// Call Model.destroy().
// We are reusing the existing functionality from Backbone.Model.destroy().
Backbone.Model.prototype.destroy.apply(this, arguments);
}
});
var m= new MyModel({ id: 123, secondaryId: 456 });
// Note: You need to set 'id' in order for destroy() call to be successful.
m.destroy({
sucess: function() { console.log('good'); },
error: function() { console.log('bad'); }
});
If you open up Firebug or Chrome Dev Tools, you should see an XHR/AJAX call was made to www.awesome.com.
Since you mentioned that you want to do this across ALL entities that you have, what you can do in that case is to create a BaseModel in your application, and have all your entities extend from it.
Anyway, hope this helps!
JSFiddle for this: http://jsfiddle.net/EwQaD/
I need to override Backbone.sync to allow PUT the problem is i don't know how and where to put it.
This is my Model:
define([
'underscore',
'backbone'
], function(_, Backbone) {
var Input = Backbone.Model.extend({
url: 'http://localhost/InterprisePOS/SOP/mobilecreateinvoice/',
initialize: function(){
},
toJSON : function() {
return _.clone({ input:this.attributes });
},
});
return Input;
});
This is my Save function in my view:
save: function(){
invoice = new Invoice();
input = new Input();
invoice.set({POSWorkstationID: "POS7"});
invoice.set({POSClerkID: "admin"});
invoice.set({CustomerName: "Alice in Wonderland Tours"});
invoice.set({IsFreightOverwrite: true});
invoice.set({BillToCode: "CUST-000009"});
InvoiceCollection.add( invoice );
//var invoices = JSON.stringify({Invoices: InvoiceCollection.toJSON()});
_.each(this.collection.models, function(cart){
InvoiceDetailCollection.add( cart );
});
input.set({ "Invoices": InvoiceCollection.toJSON() });
input.set({ "InvoiceDetails": InvoiceDetailCollection});
alert( JSON.stringify(input.toJSON()) );
input.save();
}
The default Backbone sync handler maps CRUD to REST like the following:
create → POST /collection
read → GET /collection[/id]
update → PUT /collection/id
delete → DELETE /collection/id
Sometimes older servers emulate HTTP by mimicking the HTTP method with _method and X-HTTP-Method-Override header. If that is the case, you should set Backbone.emulateHTTP to true
If you want custom mappings, then you would need to override Backbone.sync. An example of overriding could be like the following:
Backbone.sync = function(method, model, options, error) {
// Backwards compatibility with Backbone <= 0.3.3
if (typeof options == 'function') {
options = {
success: options,
error: error
};
}
var resp = function(resp) {
if (resp.status) {
options.success(method != 'read' ? model : resp.data);
}
else {
options.error('Record not found ' + resp.data);
}
};
var store = model.customStorage || model.collection.customStorage;
switch (method) {
case 'read': model.id ? store.read({id: model.id}, resp) : store.readAll(resp); break;
case 'create': store.create(model.attributes, resp); break;
case 'update': store.update(model.attributes, resp); break;
case 'delete': store.delete(model.id, resp); break;
}
};
Where customStorage is your implementation, it could be anything you want that respects the methods I created. Some time ago, I created a backbone sync override for HTML5 WebSQL Storage, it is open sourced located on GitHub https://github.com/mohamedmansour/backbone.webStorage
I hope this helps you get started! Good Luck!