I'm trying to figure out how to keep a collection of models in sync with my server. At the moment, my code looks like this, but it's clearly got a few problems...
var myCollection = Backbone.Collection.extend({
url: '/api/block',
...
sync: function() {
_.each(this.models, function(model) {
model.sync('create', model);
});
}
});
At the moment, when I call sync on my collection, it iterates through all of the models, and calls sync on each of them. I'm surprised, firstly that this isn't the default behaviour of a Backbone.Collection.sync method. Or am I misunderstanding what 'syncing collections' should do?
Secondly, my model.sync('create', model); line is a bit odd. Why do I have to pass model in when I'm calling the method on the model itself? Also, I'm manually calling 'create' which is clearly wrong. Shouldn't 'sync' be aware of whether it should created or update?
And also, shouldn't 'sync' be intelligent enough to call 'delete' on any models which have been removed from my collection?
I'm guessing I should be looking at customising the sync method on my model, but I'm still confused as to what I'm doing that is unusual that would require me to customise these sync methods.
I feel like I'm missing a fairly big point about sync.
For the record, there isn't really an answer to this question, because it's an invalid question! I misunderstood what 'sync' was for. I am now using save as KiT O has pointed out.
Thanks.
Related
I'm trying to remove a model from my collection, but I don't want to lose the view of that model on my front end (it's a summary screen).
I'm using this at the moment to remove the model:
groupChats.remove(chatid);
Is there any way to do this, or is it not possible? (I'm guessing that somewhere I can unbind the two, but no idea how to, or where it is documented).
Solution provided by Cheng Ping Onn in comments:
groupChats.remove(chatid, {silent: true});
I'm working on an analytics-like dashboard with heavy data. Obviously waiting for all this data to come in isn't a good idea as it would increase load-times beyond acceptable lengths. My idea is to progressively add parts of data when certain Controllers instantiate a model. I imagine it'll end up looking something like this:
class List.Controller extends Marionette.Controller
initialize: (options) ->
{ model } = options
model.fetch( something here ) unless model.get('data')
#showData model
getDataView: (model) ->
new List.Data {model}
showData: (model) ->
dataView = #getDataView model
App.mainRegion.show dataView
I was wondering if someone has experience with this, and what a good strategy would be for what to pass into the fetch call and how to structure that...
Edit: to clarify, I'm looking for a scalable strategy to load more data for a model based on a get-param or a different endpoint when my app needs it. Should this be handled by methods on my model or by passing stuff into fetch for example?
The "standard" way is having a pageable collection, such as Backbone Pageable
Then you can have a CollectionView or CompositeView which will handle most of the work for you.
I am using Backbone.js, Lawnchair, and backbone.lawnchair.js.
I was wondering what the correct way to "empty" a collection (both from the application and from localStorage) would be?
Currently, I am employing something along these lines:
Favorites.Collection = Backbone.Collection.extend({
model: Favorites.Model,
lawnchair: new Lawnchair({ name: "favorites" }, function(e){}),
empty: function(){
this.lawnchair.nuke();
this.fetch();
}
});
This is essentially destroying the elements in localStorage (lawnchair provides the nuke method) and fetching from localStorage. This seems a bit clunky, and I was wondering if I am thinking about this right - or if there is a better way.
Cheers!
Backbone follows sort of RESTish principles and sadly REST doesn't describe a bulk-delete procedure. Resources can only be deleted one at a time. Typically APIs don't support HTTP DELETE on the entire collection URI like DELETE /favorites, only DELETE /favorites/42 - one at a time. Thus there's no single backbone method that just does this as generally the collection is mostly designed to do fetch/GET and then delegate to the individual models to do saves and deletes. So yes, you're on your own for a bulk-delete approach. You can do something more RPC-like and pass a list of IDs to a delete procedure, but what you have above seems perfectly adequate to me, and yes deleting them all directly and then re-fetching the collection seems also very reasonable to me. So I think you're solution is fine as is, but I'm also curious to see what others suggest.
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'm just trying to figure out a probably simple question.
Should views set model data directly or only call model methods that change their own data?
like everything else in software development, "it depends".
if you're using form inputs in your views and you just need to get the data from those inputs in to the models, set the data directly. You can do this any number of ways, including "change" events from the input fields, like this:
MyView = Backbone.View.extend({
events: {
"change #name", "setName"
},
setName: function(e){
var val = $(e.currentTarget).val();
this.model.set({name: val});
}
});
On the other hand, if you're kicking off business logic and other code that happens to set the data in the model (but really only does this as part of the business logic), then you should call a method on the model.
A "state machine" would be a good example of when you would do this. Or, in an image gallery that I wrote, I had some logic around the selection of an image for display. If an image is already selected, don't select it again. I put this logic in a method on my image model:
Image = Backbone.Model.extend({
select: function(){
if (!this.get("selected")){
this.set({selected: true});
}
}
});
As illustrated here, I like to run by the simple rule that if I have zero logic around the call to set, then I set it directly from wherever I am. If there is any logic related to the model, around the set, then I put it in the model.
Either way, when you do want to set data, you should use the set method. Bypassing this and setting the model's attributes directly through model.attributes will prevent a lot of Backbone's code from firing and potentially cause problems for you.
That depends on your programming style. I always set them directly. If the Law of Demeter sounds good to you and you're into object orientation (and possibly with a Java/Microsoft -stack background), then the style would be to create getter/setter methods.
If you on the other hand is in the "Size is the Enemy" camp (I also recommend Jeff Atwood's comments) the you certainly should set model data directly (as I hinted before, I'm in this camp myself).
That being said, Backbone.js models already have getter and setter methods .get and .set. You should not manipulate the .attributes member directly. I'm not as sure you shouldn't read from it, I tend to do that now and then and haven't run problems because of that yet.