What technique shall one use to implement batch insert/update for Backbone.sync?
I guess it depends on your usage scenarios, and how much you want to change the calling code. I think you have two options:
Option 1: No Change to client (calling) code
Oddly enough the annotated source for Backbone.sync gives 'batching' as a possible reason for overriding the sync method:
Use setTimeout to batch rapid-fire updates into a single request.
Instead of actually saving on sync, add the request to a queue, and only batch-save every so often. _.throttle or _.delay might help you here.
Option 2: Change client code
Alternatively, instead of calling save on your models, you could add some sort of save method to collections. You'd have to track which models were actually modified and hence in need of update, since as far as I can tell, Backbone only knows whether they're new or not (but I could be wrong about that).
Here's how I did it
Backbone.originalSync = Backbone.sync;
Backbone.sync = function (method, model, options) {
//
// code to extend sync
//
// calling original sync
Backbone.originalSync(method, model, options);
}
works fine for me , and I use it to control every ajax request coming out of any model or collection
Related
I'm trying to find a generic solution to commit/rollback a model alongside ng-resource.
When to commit/save model:
Successful http write methods (PUT, POST, PATCH).
When to rollback/reset model:
Failing http write methods.
User deciding to cancel their local changes (before PUT).
I've found pieces of this solution scattered, but no comprehensive strategy.
My initial strategy was to make use of the default ng-resource cache. Controller starts with GETting an item. If a subsequent PUT/POST/PATCH failed (caught either in the controller's promise, or in the $resource.<write_method>'s interceptor.responseError, re-run the GET that first got the object, and replace my model with that, as on initial load. However, this doesn't help if there's a successful PUT, followed by a failed one, because I'd be replacing my model with a stale object. The same problem occurs when a user tries to cancel their form changes before submitting... the last successful GET may be stale.
I've got a model with ~10 properties, and multiple <form>s (for html/design's sake) that all interact with this one object that gets PUT upon any of the forms being submitted:
{'location_id': 1234,
'address': {
'line1': '123 Fake St'}
...
},
'phone': '707-123-4567',
...
}
Models already have nice rollback features, but getting ng-resource to touch them seems tricky, and I really want to reset the entire object/model. Using a custom cache alongside my $resource instance seems like a decent way to go, but is equally tricky to give user-interaction a way to rollback. Another idea was to store a separate object when loading into scope: $scope.location = respData; $scope._location = angular.copy(respData); that I could load from when wanting to roll back, by way of $scope.location = $scope._location;, but I really don't want to clutter my controllers site-wide with these shadow objects/functions (DRY).
What is a general good practice for when some action - changes multiple models in Backbone.js:
Trigger multiple PUT requests for each mode.save()
Single request to sync the entire collection
In case if the quantity of the changed models greater than 1 - definitely it should be the second item.
Usually, good REST api practice seems to suggest that you should update, save, create, delete single instances of persistent elements. In fact, you will find that a Backbone.Collection object does not implement these methods.
Also, if you use a standard URI scheme for your data access point, you will notice that a collection does not have a unique id.
GET /models //to get all items,
GET /models/:id //to read an element,
PUT /models/:id //to update an element,
POST /models/:id //to create an element,
DELETE /models/:id //to delete an element.
If you need to update every model of a collection on the server at once, maybe you need to ask why and there might be some re-thinking of the model structure. Maybe there should be a separate model holding that common information.
As suggested by Bart, you could implement a PATCH method to update only changed attributes of a particular element, thus saving bandwidth.
I like the first option, but I'd recommend you implement a PATCH behavior (only send updated attributes) to keep the requests as small as possible. This method gives you a more native "auto-save" feel like Google Docs. Of course, this all depends on your app and what you are doing.
According the Backbone.js documentation Model-parse does the following:
parse is called whenever a model's data is returned by the server, in
fetch, and save.
To augment models I've already loaded I use Model.parse(). I accomplish this by using fetch to make an additional request for data, then use that data to add properties to an existing model.
Example:
the fetch object is {age: 19}
after the parser will be {age: 19, isAdult: true}
When I perform the save request, in the PUT request I also have other parameters not needed (for example isAdult). I would like to have the original model (without additional parameters in PUT request).
What is the best way to achieve my goal in Backbone?
If I understand your question correctly ....
When backbone talks to a server using a save it sends a complete respresentation of the model. As the docs put it :
The attributes hash (as in set) should contain the attributes you'd
like to change — keys that aren't mentioned won't be altered — but, a
complete representation of the resource will be sent to the server.
So the default behavior is to send the complete model. If you want to implement you're own logic you're going to have to override the sync method. Dig through the expanded backbone code a bit and you'll see this comment above sync :
// Override this function to change the manner in which Backbone persists
// models to the server. You will be passed the type of request, and the model in question.
I would use the default implementation of sync as my starting point.
I read in the backbone documentation that calling collection.reset() clears the collection. I want to know if it removes the models as well or do they continue to live in memory?
If they're not removed is there an easier way to remove all the models in a collection without iterating through the models and calling model.remove()?
You could listen for the reset event from the model and do your cleanup and this.destroy() in response. That's what the event hooks are for. See http://backbonejs.org/#Events-catalog
Note: You absolutely should not change or override any method or property prefixed by an underscore, such as _removeReference. The underscores mean that it is intended as an internal method or property, and that the internal implementations may change (their API's are considered unstable). Upgrading Backbone could break any code that relies on underscore-prefixed methods, even if the release is advertised as a backwards-compatible change.
I know your question says "without iterating", but it really is the most reliable way of handling this. Consider a case where a model has been moved from one collection to another, but it's still listening on the first collection's reset event (because a programmer six months later didn't notice the connection).
Now when the first collection gets reset, the moved model gets destroyed. Oops!
Iterating over the collection probably is the best way to handle this if you don't have an endpoint on your API that will delete all objects in a collection in batch on the API server (which is often how this is handled).
Luckily, that iteration is pretty easy:
destroyAll: function () {
var promises = [];
while(this.models.length > 0) {
promises.push( this.models[0].destroy() );
}
// handle errors communicating with the server
$.when(promises).fail(function (response) {
this.trigger('syncError',
response);
}.bind(this));
}
What you are looking for is, probably, for the models to be garbage-collected. That is, that nobody has a reference to these models anymore, after they are removed from the collection.
Backbone does its part of removing the references that it set on the models, when they are removed from the collection. However, you have to do your own cleanup if your code has references to those models. Most of the time, this happens if those models are registered as event listeners, like in this example: http://jsfiddle.net/dira/4uxp4/2/
Taking a look at the implementation of reset, you could change _removeReference to call a cleanup function on the model as well. And in the model, remove the model from all the listeners/all the other objects that keep a reference to it.
i need some help
which is when i on my index.ctp/view.ctp, i need to call to my controller function to perform some task. what code i can use to perform this action?
i need to call to my controller function, which send in a value (user_id) to the function and get me a certain action. how can i do that? i might calling in a javascript function as well.
If you need to call a Controller function from the View, you're doing it wrong. It's not proper MVC.
Having said that, requestAction would be the proper, albeit slowest way to do so. You could hack around a bit more and get an instance of the Controller from the ClassRegistry. But I'd seriously recommend you to restructure your program flow so you don't need to do this to begin with.
You should probably perform the task in the controller before you get the view. But if you need to do some view work on the data you are displaying you might want to consider making a Helper class.
http://book.cakephp.org/view/101/Creating-Helpers
If your task doesn't generate any output - you might want to consider doing it in the controller before you even get to the view stage.
If your task has some form of output - use requestAction with a view Element
http://bakery.cakephp.org/articles/view/creating-reusable-elements-with-requestaction
That link should be a good starting point. There are also good posts by Mark Story on his blog that detail the actual performance of requestAction and it really isn't that bad if you don't abuse it all over the place.
http://mark-story.com/posts/view/how-using-requestaction-increased-performance-on-my-site
http://mark-story.com/nodes/view/reducing-requestaction-use-in-your-cakephp-sites-with-fat-models
If you really need to trigger some sort of logic in a predictable way and that logic might happen in more than one place you can also use an event observer pattern to trigger the controller action you need to run.
http://cakealot.com/2009/04/eventful-a-cakephp-event-system/