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});
Related
Writing my first Backbone app - came across a predicament wherein i am unable to choose which is the best way to move forward.
Scenario : User clicks an edit button , a new view is loaded . Approach is as below.
renderEditView: function(){
if(my.namespace.view){
my.namespace.view.render();
}else{
my.namespace.view= new editView({model:my.namespace.model});
}
}
Basically, i am assigning my view to a namespaced variable and resuing it as required. Didn't face any problems as such.
But some advocate recreating the View again using new editView({model:xxx}); whenever the edit button is clicked . i Would like to know which one is the better practice and why?
P.S: i am aware of the 'event ghosting' problem in BB apps, and the excellent solution provided by Derick Bailey .But still would love to know the pros and cons between the approaches.
This is indeed an opinion, because either way will work if you (as you mention) take care of cleaning up previous views if you decide to instantiate a new one every time you want to re-render. It's important to avoid duplicating lingering events from every instance that you want to replace by creating a new one.
Personally I have used both strategies and never had problems with them so far.
When re-using a view, I bind the view as a property to the controller object that renders the view, pretty much the same way you do it.
Theoretically, I don't see a reason to re-instantiate a view if it was already created before. It isn't that you really require a new instance, it's just that you want to re-render it.
Sidenote
For re-rendering views, Backbone Marionette offers regions, which are convenience objects that allow you to do things like:
var myView = new MyView();
var region = new Marionette.Region({el: "#container"});
region.show(myView);
In case you would decide to instantiate a new view every time, these regions take care that previously rendered views are properly cleaned up.
I am trying to implement pagination with backbone.marionette's
compositeView by first doing an initial fetch:
collection.fetch({reset: true})
To load additional models into the same collection,
collection.fetch({reset: false, remove: false})
is used.
I saw that on each use of fetch() with {reset: false}
muliple add events are triggered (for every model added.)
Thats fine basically, but when looking into the annotated source
of the compositeView's appendHtml function in marionette the follwing
statement is given:
buffering happens on reset events and initial renders in order to
reduce the number of inserts into the document, which are expensive.
While this is great for the initial fetch (only 1 browser reflow), every
subsequent fetch with {reset: false} could get pretty expensive
(reflow on every single model thats added). It really seems to
append() the itemView every single time - causing a reflow
(compositeView.isBuffering is false).
Is there any way to "batch" process the models and do a single insert
(same as done on the reset event)?
Does this need an overwrite in the appendHtml implementation
and implement the batch logic by myself?
(I assume you don't want to use Backbone.paginator, for some reason. If that isn't the case, use the plugin ;-D).
You could use a technique similar to the filtered collection used here: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/entities/common.js#L2 (instanciation at https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L13).
Basically: you provide a "paginated collection" to the view, and what the paginated collection does is:
initially, return a "cloned" copy of the original collection
when you fetch on the original collection, add the fetched events in "batch" to the cloned collection, which will trigger the view to refresh (because it's listening to the cloned collection, not the original)
To minimize reflows, you can try the following:
silence the events that the collection listens to ("add", etc.)
when a group of models gets added, create a new instance of a collection view with only the new models
render the new collection view (but don't show it anywhere), and manually append the newView.el to the right place in the DOM
destroy the newView
Unfortunately, once you need to start optimizing, you tend to need to handle a bunch of things by hand...
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.
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'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.