Initialize CollectionView with a collection reference - backbone.js

In my initialize function of a CollectionView Backbone Marionette object, I'm doing the following:
this.collection_addresses = new AddressCollectionView({
el: 'ul.addresses',
collection: options.user.get("addresses")
});
However the AddressCollectionView is never updated whenever the object referenced in options.user.get("addresses") is changed, I thought Marionnette is handling this automatically. The user object is updated using a fetch. Any thoughts?
Edit #1
Just to clarify the collection view is like this
var AddressCollectionView = Backbone.Marionette.CollectionView.extend({
itemView: AddressItemView,
tagName: 'ul'
});
Thanks.

from the v1.0.3 source code, here are the events the collectionview listens to
_initialEvents: function(){
if (this.collection){
this.listenTo(this.collection, "add", this.addChildView, this);
this.listenTo(this.collection, "remove", this.removeItemView, this);
this.listenTo(this.collection, "reset", this.render, this);
}
},
A collectionView accepts an itemView view object definition, (not an instance). This itemView is initialized with the model for each item in the collection by the collectionview. I would extend marionette's itemview class, and listen to your change event on your model there, and inject that itemview view object definition, (not an instance) into the collectionview.

Related

Marionette collection view does not update when model added

I am new to Marionette. So may be its pretty basic.
I have a collection initialized globally and is empty during creation.
App.Colors= Backbone.Collection.extend({
model : App.Color,
comparator : "price"
});
var appColors = new App.Colors();
If I create a Marionette collection view with this empty collection.
var colorview= new App.ColorView({collection:appColors});
and later on add to the collection appColors.
appColors.add({code:'red'})
Shouldn't the view be updated automatically since it listens to collection.add. I know it will work fine if I type:
colorview.collection.add({code:'red'})
In order for it to work, you must bind the view rendering to the collection add event, so basicly in your view declaration you must have something like this :
App.ColorView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection, 'add', this.render);
...
}
...
});

Backbone js bind method on collection

I am following the tutorial at:
http://arturadib.com/hello-backbonejs/docs/3.html
This is a piece of code I am stuck on:
initialize: function(){
_.bindAll(this, 'render', 'addItem', 'appendItem'); // remember: every function that uses 'this' as the current object should be in here
this.collection = new List();
this.collection.bind('add', this.appendItem); // collection event binder
this.counter = 0;
this.render();
},
The line of code that I am having a hard time understanding is:
this.collection.bind('add', this.appendItem);
I know there is a bind method in underscore, but I don't think that is the same bind function.
Could you explain what the above line is for and where I can read more about it?
In backbonejs, a collection can trigger events. Example:
this.collection.trigger('myEvent');
In addition, you can also bind a collection to some events. Example:
this.collection.bind('myEvent', function() { ... });
Backbone.Collection.bind() method comes from Backbone.Events. Note that Backbone.Collection has all the methods of Backbone.Events mixed in (like all the other backbone.js objects including Backbone itself).
Backbone.Events.bind() is an alias for Backbone.Events.on().

Bind a view to collections in backbone.js

I'm semi-new to backbone. I'm trying to bind a collection to a view so that when a new model is added to a collection, the view is updated. I think when you do this with models you can bind to the model's change event. But how do you do the same with collections?
App.Views.Hotels = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(this.addOne, this);
var floorplanView = new App.Views.Floorplans({collection:floorplanCollection});
$('.floorplans').html(floorplanView.render().el);
return this;
},
events: {'click': 'addfloorplan'},
addOne: function(hotel) {
var hotelView = new App.Views.Hotel ({model:hotel});
this.$el.append(hotelView.render().el);
},
addfloorplan: function() {
floorplanCollection.add({"name": "another floorplan"});
}
});
App.Collections.Floorplans = Backbone.Collection.extend({
model: App.Models.Floorplan,
initialize: function () {
this.bind( "add", function() {console.log("added");} );
}
});
The click event fires and adds to the collection. But how do I get it to update the view?
You can listen to the collection's add event, which fires when a new item is added to the collection. In modern versions of Backbone, the method listenTo is preferred to bind or on for listening to events. (Read de documentation for more info)
For example, in your case this should do the trick:
App.Views.Hotels = Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection,'add', this.addOne);
},
//rest of code
Hope this helps!
Here is a nice tutorial I had followed long ago.
An Intro to Backbone.js: Part 3 – Binding a Collection to a View
It helps you define a DonutCollectionView that will, when given a collection of donuts, render an UpdatingDonutView for each donut.

Backbone: collection event binder

This is a 5 part backbone.js hello world tutorial/application. https://web.archive.org/web/20180317062059/http://arturadib.com/hello-backbonejs/docs/3.html In part 3, the author's illustrating how to use collections and models to store data and how to tie changes to the views.
I understand most of it, except this line
this.collection.bind('add', this.appendItem); // collection event binder
Is this 'bind' just binding context, or is it functioning as an 'event' such appendItem is called anytime that a model has been added?
I ask, because in the render method of the ListView, it's explicitly calling appendItem method, so why is it bound to 'add'
_(this.collection.models).each(function(item){ // in case collection is not empty
self.appendItem(item);
}, this);
Can someone please explain a little how that code is working. i looked through the documentation but couldn't find an explanation of bind used in this way.
Full code
(function($){
¶
Item class: The atomic part of our Model. A model is basically a Javascript object, i.e. key-value pairs, with some helper functions to handle event triggering, persistence, etc.
var Item = Backbone.Model.extend({
defaults: {
part1: 'hello',
part2: 'world'
}
});
¶
List class: A collection of Items. Basically an array of Model objects with some helper functions.
var List = Backbone.Collection.extend({
model: Item
});
var ListView = Backbone.View.extend({
el: $('body'),
events: {
'click button#add': 'addItem'
},
¶
initialize() now instantiates a Collection, and binds its add event to own method appendItem. (Recall that Backbone doesn't offer a separate Controller for bindings...).
initialize: function(){
_.bindAll(this, 'render', 'addItem', 'appendItem'); // remember: every function that uses 'this' as the current object should be in here
this.collection = new List();
this.collection.bind('add', this.appendItem); // collection event binder
this.counter = 0;
this.render();
},
render: function(){
¶
Save reference to this so it can be accessed from within the scope of the callback below
var self = this;
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<ul></ul>");
_(this.collection.models).each(function(item){ // in case collection is not empty
self.appendItem(item);
}, this);
},
¶
addItem() now deals solely with models/collections. View updates are delegated to the add event listener appendItem() below.
addItem: function(){
this.counter++;
var item = new Item();
item.set({
part2: item.get('part2') + this.counter // modify item defaults
});
this.collection.add(item); // add item to collection; view is updated via event 'add'
},
¶
appendItem() is triggered by the collection event add, and handles the visual update.
appendItem: function(item){
$('ul', this.el).append("<li>"+item.get('part1')+" "+item.get('part2')+"</li>");
}
});
var listView = new ListView();
})(jQuery);
Say your collection already has 10 models. Then you pass it to your view. You'd call render() which triggers a loop of appendItem() or what not. Your view is happy.
Then you add a model to your collection. (Model 11)
Rather than re-render the whole thing, the this.collection.on('add', this.appendItem, this) executes the function that adds a single item view to the already existing view list.
That's probably why it's bound to the add event AND included in the render as a loop. One loops through an existing collection to generate views at the start. One takes care of any new models that are added after the view is initialized and rendered the first time.

why do bindAll in backbone.js views?

In backbone's todo demo the code has a few spots where _.bindAll(this,...) is used. Specifically it's used in the initialize function of both views. As far as I can tell it's necessary to do the following:
this.$('.todo-content').text(content);
But why would one want to do the above, when one can do:
$('.todo-content').text(content);
?
_.bindAll( this, ... ) is necessary not only for this.$( selector ).doSomething() but generally to be sure that this in your view's method is always pointing to the view itself.
For example, if we want to refresh our view when the model changes, we bind the view's render method to the model's change event:
initialize: function() {
this.model.bind( 'change', this.render );
},
Without _.bindAll( this, 'render' ), when the model changes this in render will be pointing to the model, not to the view, so we won't have neither this.el nor this.$ or any other view's properties available.
As of Backbone 0.5.2, it's no longer necessary to use _.bindAll(this...) in your views to set the context of the "bind" callback functions, as you can now pass a 3rd argument to bind() that will set the context (i.e. "this") of the callback.
For example:
var MyView = Backbone.View.extend({
initialize: function(){
this.model.bind('change', this.render, this);
},
render: function(){
// "this" is correctly set to the instance of MyView
}
});
this.$ limits jQuery's context to the view's element, so operations are quicker.
Additionaly, this.$('.todo-item') won't find your elements with todo-item class outside your view's element.

Resources