Backbone PUT request explicitly with out using id - backbone.js

I'm trying to PUT the data and my model doesn't have an id.
Is it possible to explicitly tell the Save() method to PUT the data irrespective of ID.

The save method has an options parameter that can override anything on the XHR:
model.save(newVals, { type: 'PUT' })
You can also override the isNew method. PUT vs POST is determined by the result of that method. You'll also want to make sure the URL is being created correctly for new and non-new objects.
Also consider setting the idAttribute correctly so that your model does have an id field that can be used to generate a correct url. Using POST and PUT correctly (POST new items, PUT updates to items) makes your api more intuitive.

Related

How to Restangularize items in collection off parent object

I am returning an object using Restangular over AngularJS (GPT object is the parent object being returned), with an array being returned corresponding to the projects withing the GPT.
I can do all the Restangular "stuff" like save() etc. on the parent GPT object. However, when I get a reference to individual items in the "projects" collection, I am unable to do a Restangular save() on it. How do I make sure all items returned in the collections below the main object are "Restangularised" so I can perform restful operations on them? i.e. I guess I want a "deep Restangularisation" if that makes sense ;-)...if not, how do I Restangularize an instance just before I attempt to do a save() operation and provide the relevant URL for the PUT/POST etc.
Hope this makes sense.
Regards
i
There is a Restangular.restangularizeElement method.
You can use it as follows (for collection):
Restangular.one('courses', 123).get().then(function (course) {
course.students = Restangular.restangularizeCollection(course, course.students, 'students');
// You should now be able to do 'course.students[0].remove()'
// And if you want to chain promises:
return course;
});
source

Backbone try to update model in server after save

I use Rails 4 + backbone in my application.
Everything is good. New model is created in backbone and saved by calling:
newItem.save(null, {success: this.sendSuccess, error: this.sendError});
However, implementing a new feature I need to change one of the model attributes. What I see that a PUT action is fired just before sendSuccess is called, which I want to avoid.
Moreover, the url is very strange. Save action calls this url:
Remote Address:127.0.0.1:3000
Request URL:http://www.lvh.me:3000/api/user/1/tickets
Request Method:POST
and then, after server return the json with the modified attribute, backbone calls this url:
Remote Address:127.0.0.1:3000
Request URL:http://www.lvh.me:3000/api/user/1/tickets
Request Method:PUT
without the ticket id!
Is there any way to prevent backbone fire an update when server return the model with different attributes?
The problem was that I had a listener in my model exactly on the column that server changed:
initialize: function() {
this.on("change:status", this.statusChanged, this);
},
Now I had to figure out why the update url does not contain the model id.
I figured out that when I first created the model, from some reasons I couldn't assign it to the collection, so in order to save it I assign the url manually:
var newTicket = new MyApp.Ticket( ticketData );
newTicket.url = this.collection.url();
Now, the bug is that url is a function, and I simply overrided it!
I changed the code to:
newTicket.urlRoot = this.collection.url();
and now it works.
Backbone will always perform PUT if your model has an id attribute setted. Which makes sense when using RESTfull.
Be sure that you're really SAVING(new model withoud an ID) a data to server instead of UPDATING(model with an ID) to server.

Restangular put loses part of the url

I want to modify one object via a REST API using Restangular on the client side. I do the following:
Restangular.setBaseUrl('/api/v1');
return Restangular.one('lists', item.listId).one('items',item.order).get().then(function(elem) {
elem.text = item.text;
elem.put();
});
But the put method gets the wrong URL missing the ID for the last member (item.order).
The expected url is /lists/2/items/22 but i get /lists/2/items and the PUT fails.
What could I be doing wrong?
Ok, let me try a theory :)
My comment above was a bit lame because I was focusing on the first part of the code and not the put().
The first part is perfect, you tell Restangular to build the URL with one() methods and the GET request must be very fine.
Then, base on a new Restangular-aware object, you try to persist the change in your API.
However, I believe that Restangular does not actually know what field to use as id in order to build the right query for your elem object.
Did you try to configure, in your Restangular Entity Provider the fields property?
RestangularProvider.setRestangularFields({
id: "orderId",
});
I believe that specifying the right field as id would suffice.
As a bonus, it seems that .save() will use PUT or POST accordingly if it's a new object or not.
Hope this helps

Where does information in BackBone go?

When a save, or create is tossed towards the server, the server responds with a new randomly created object. The object can be one of many different Classes, and Backbone responds to these differentiating objects and loads a relative view.
I can only seem to figure this logic out on bootstrap, as no view has been loaded yet, so I can based on what information I am randomly receiving from the server, bootstrap and navigate to that specific route.
However, I am stuck on trying to figure out how to do this when I save an object, and receive my return data.
Here's my code broken down.
The information is saved.
#model.save(#model.toJSON(),
I have a listenener waiting for this save :
constructor: (options) ->
super(options)
#model.bind 'change:verb', _.chooser, options
_.maestra_chooser is a mixin I have in a utility belt :
_.mixin
_chooser : (item) =>
console.log item
Something to note here. The variable item is unfortunately, the same #model that was just saved. No new data there.
What I'm hoping for item to be is the new variable data from the server, so that I can take that data, see what kind of data it is, and then route to the relevant view.
This is where I believe I'm also making an architecturally unsound idea. But for reasons I don't understand enough to explain.
Does anyone know where I can access the return data from the server and appropriately navigate my app to that respective route?
Additional Information
This is how I bootstrap it appropriately :
window.router = new Project.Routers.QuestionsRouter(
{
words: #{ #words.to_json.html_safe }
});
Backbone.history.start();
router.navigate("#{#words.kind_of?(Array) ? "bar" : "foo"}", {trigger: true, replace: true})
The change event is only ever going to give you the model and the value that changed...
You can pass a success callback to your save:
#model.save(#model.toJSON(), success: (model, resp) ->
# do whatever with resp
)
where resp will contain the raw response from the server and model will contain the server side state of your model.
You can also bind to your model's sync event as mentioned in the comments:
#model.bind 'sync', _.masetra_chooser, options
the sync callback is called with arguments: model, resp and options where options is the set of options passed to save.
https://github.com/documentcloud/backbone/blob/9a12b7640f07839134e979b66df658b70e6e4fe9/backbone.js#L383
Not really sure why you are expecting to get data back from a save that'll change your page though. Seems a bit odd.
What type of data are you expecting to receive after a save that wouldn't be in your model?

PATCH request method in Backbone.js

What is the right way to perform PATCH request while saving model's attributes in Backbone.js?
As of Backbone.js v0.9.9, you can simply pass { patch: true } to save().
Read more: http://backbonejs.org/#changelog
In addition to James Cropchos answer I want add the following, because this stole some hours from me and maybe helps someone else:
If you use model.save(attributesToPatchObject,{patch: true}) like it is possible since backbone v.0.9.9 as stated in James Cropchos answer, you may wonder how to determine which attributes have changed since the last call of model.save() to pass them in as attributesToPatchObject which is the first argument from model.save() (or model.fetch() if you didn't save the model lately).
Backbone itself didn't keep track of those attributes. I thought the method model.changedAttributes() could fit, but as the backbone-doc says this method returns
a hash of only the model's attributes that have changed since the last
set, or false if there are none
So this method didn't fit for this need. After some research I found out that backbone itself didn't keep track of unsaved attributes (I know, not a brilliant finding if I had read the docs more carefully).
I found out that backbone.trackit is a backbone plugin which exactly add the needed feature to backbone, by adding the method unsavedAttributes() to the model. The docs of backbone.trackit says about this method:
Symmetric to Backbone's model.changedAttributes(), except that this returns a hash of the model's attributes that have changed since the last save, or false if there are none. Like changedAttributes, an external attributes hash can be passed in, returning the attributes in that hash which differ from the model.
It works like this:
//fetch an existing model from server
model.fetch({
success : function(model, respose, options) {
//tell backbone.trackit to track unsaved Attributes
model.startTracking();
}
});
//now some changes to the model happen
model.set("someProperty", "someValue");
/* save the model to server using the PATCH-Method
and only send the unsaved Attributes;
in this case only "someProperty" is sent
*/
model.save(model.unsavedAttributes(), {patch: true});
Since the unsavedAttributes() returns false if there are no unsaved Attributes, you could additionally wrap your save() statement within an if-condition which checks if unsavedAttributes() returns something other then false and only do your PATCH-Request if it's needed (because something changed).
NOTE: You didn't have to call fetch() to use startTracking() so you can use this method even with newly created models (model.isNew() returns true on that model), if there is a usecase for that.
Hopes this may save someone a little bit of research time.
You'll have to override Backbone.sync and extend the existing method mapper
var methodMap = {
'create': 'POST',
'update': 'PUT',
'delete': 'DELETE',
'read': 'GET',
'patch': 'PATCH'
};
you'll have to create your own patch method on a model like
Backbone.Model.prototype.patch = function(options)
{
// some code here that checks what attributes have changed since last save
var xhr = (this.sync || Backbone.sync).call(this, 'patch', this, options);
return xhr;
}
I'm sure you can extend Backbone further to include OPTIONS and HEAD if you needed to
Note though, that even through jQuery supports the PATCH, OPTIONS and HEAD methods, your end-users' browser may not.

Resources