Marionette composite View appendHtml not triggered - marionette

Consider the following Marionette composite View. Does anyone know wy appendHtml event does Not fire:
var TreeView = Backbone.Marionette.CompositeView.extend({
template: "#node-template",
tagName: "ul",
initialize: function(){
this.collection = this.model.nodes;
},
appendHtml: function(collectionView, itemView){
alert('appendHtml triggered');
collectionView.$("li:first").append(itemView.el);
}
});
look at the alert('appendHtml triggered'); Why does it not fire?
Has that been removed?

Look at the docs. Depending on the version of Marionette you are using. Use attachHtml()

Related

Marionette itemViewEvents; event dispatching and binding

I'm having some trouble understanding how event dispatching and binding to events between children parents work in the awesomeness that is Marionette.
Is it correct that I can trigger a custom event from an itemView like this:
var Item = Marionette.ItemView.extend({
events: {
"click .foo": "do:something"
}
});
var itemCollection = Marionette.CollectionView.extend({
itemView: item,
initialize: function () {
this.on("itemview:do:something", this.onSomething, this);
}
}};
Is there some shortcut to binding to the itemView events like I would DOM events:
var itemCollection = Marionette.CollectionView.extend({
itemView: item,
itemviewevents: {
"itemview:do:something": "onSomething"
}
}};
Thanks :).
You're confusing triggers and events. Your code should be
var Item = Marionette.ItemView.extend({
triggers: {
"click .foo": "do:something"
}
});
Use the events hash to have a function called when an event takes place, use the triggers hash to have a trigger executed.

Listen to sub-sub views in Backbone Marionette

I have a collection view, and each item view is a composite view, which also has a item view. I want to listen to the events from this last (sub-sub) item view.
View.Block = Marionette.Layout.extend({
triggers: {
'click .content': 'block:click'
}
});
View.Category = Marionette.CompositeView.extend({
itemView: View.Block
});
View.Categories = Marionette.CollectionView.extend({
itemView: View.Category
});
In my controller I only have a reference to View.Categories:
var categories_view = new View.Categories({
collection: categories
});
Is it possible to listen to click events from View.Block using Marionette's built-in view events? I tried categories_view.on('itemview:block:click') but that won't work as View.Block isn't an item view of View.Categories, but of View.Category.
You'll need to use a module- or application-level event aggregator to achieve your objective:
events: {
"click .something": "triggerEvent"
},
triggerEvent: function(e){
myApp.trigger("something:clicked", e);
}
(myApp is the instance of your Marionette application.)
Then listen for that event:
myApp.on("something:clicked", function(e){
e.preventDefault();
console.log("something was clicked");
});

backbone click event fires as many times as there is views

I searched info on this topic but found only info about getting event element.
Yes, I can get an element of clicked div, but why it's fired all 19 times? (it's the number of views total). Info of clicked event is same - of the clicked div.
Here is what divs look like: http://d.pr/i/AbJP
Here is console.log: http://d.pr/i/zncs
Here is the code of index.js
$(function () {
var Table = new Backbone.Collection;
var TrModel = Backbone.Model.extend({
defaults: {
id: '0',
name: 'defaultName'
},
initialize: function () {
this.view = new Tr({model: this, collection: Table});
this.on('destroy', function () {
this.view.remove();
})
}
});
var Tr = Backbone.View.extend({
el: $('.pop-tags').find('.container'),
template: _.template($('#td_template').html()),
events: {
'click .tag': 'clicked'
},
clicked: function (event) {
event.preventDefault();
event.stopPropagation();
console.log(event.currentTarget);
},
initialize: function () {
this.render();
},
render: function () {
this.$el.append(this.template(this.model.attributes));
return this;
}
});
for (var i = 0, size = 19; i < size; i++) {
var trModel = new TrModel({id: i, name: 'name_' + i});
Table.add(trModel);
}
});
How can I avoid all elements from firing an event and fire only one that clicked and only 1 time?
el: $('.pop-tags').find('.container'),
Don't do that. You are attaching every view instance to the same DOM node. Each backbone view needs a distinct DOM node or, as you see, delegate events become complete chaos. In your view, set tagName: 'tr', then when creating your views, create them, call .render() and then append them to the DOM with something like $('.table-where-views-go').append(trView.el);.
You also may want to brush up on the basic MVC concept because Tables and Rows are view-related notions, not model-related, so a class called TrModel is a code smell that you aren't clear on Model vs View.
I would use a slightly different approach to solve your problem.
Instead for one view for every tr I would create one view for the entire table.
When I create the view I would pass the collection containing the 19 models to the view and in view.initialize use the collection to render the rows.
I created a jsbin with a working example.

Backbone.Marionette: Pass data down through a CompositeView to its itemView?

I'm wondering if/how a CompositeView can pass data down into its defined itemView. Example (reduced) code:
var TableView = Backbone.Marionette.CompositeView.extend({
template: '#table-template',
itemView: TableRowView,
itemViewContainer: 'tbody',
});
var TableRowView = Backbone.Marionette.ItemView.extend({
tagName: 'tr',
template: '#table-row-template',
serializeData: function () {
var data = {
model: this.model,
// FIXME This should really only be called once. Pass into TableView, and down into TableRowView?
// That way, getDisplayColumns can be moved to the collection as well, where it makes more sense for it to belong.
columns: this.model.getDisplayColumns()
};
return data;
}
});
I'm using the two to render a html table. #table-row-template has some render logic for supporting different types of "columns". This allows me to use the same views for different types of Collections/Models (as long as they follow the API). So far, it's working pretty well!
However, as you can see above, each "row" makes a call to get the same "columns" data every time, when really I just wanted to pass that on down once, and use for all.
Recommendations?
Thanks!
You can use itemViewOptions either as an object or a function
var TableView = Backbone.Marionette.CompositeView.extend({
template: '#table-template',
itemView: TableRowView,
itemViewContainer: 'tbody',
itemViewOptions: {
columns: SOMEOBJECTORVALUE
}
});
OR
var TableView = Backbone.Marionette.CompositeView.extend({
template: '#table-template',
itemView: TableRowView,
itemViewContainer: 'tbody',
itemViewOptions: function(model,index){
return{
columns: SOMEOBJECTORVALUE
}
}
});
and then receive the options with:
var TableRowView = Backbone.Marionette.ItemView.extend({
tagName: 'tr',
template: '#table-row-template',
initialize: function(options){
this.columns = options.columns;
}
});
(* Note that itemView, itemViewContainer and itemViewOptions are changed in version 2 to childView , childViewContainer and childViewOptions).

Should nested views be layout views?

A simple and short question: If a view contains two or more sub views. Should the view container be a layout view?
If not, what are good alternatives?
Update:
my code:
var LikeButtonModal = Backbone.Model.extend({
url: 'api/profile/like/'
});
var LikeButton = Backbone.Marionette.ItemView.extend({
tagName: 'button',
className: 'like',
template: '<div>like</div>',
events: {
'click' : 'like'
},
initialize: function(userId){
this.model = new LikeButtonModal();
},
like: function(){
this.model.save();
}
})
var LeftProfileView = Backbone.Marionette.Layout.extend({
template: '#profile-left',
regions:{
extra : '.extra'
},
initialize: function(){
this.on("item:rendered", this.editable, this);
},
onRender: function(){
if(this.model.get('userid') != ActiveUser.get('userid')){
this.extra.show(new LikeButton(this.model.get('userid')));
}
}
});
Layouts are good for this if you will be replacing the sub-views at different times, or if the sub-views are very different types... for example, a layout might contain your header, your navigation and your main content region.
Other options are CollectionViews and CompositeViews.
Collection views will render a collection of items, using the same type of view for each item in your collection. This works well for lists of things.
CompositeViews are CollectionViews that can render a wrapper template around the collection. For example, an HTML table structure. The table, thead, tbody and tfooter tags can be rendered in the CompositeView's wrapper template, and then a collection of items can be rendered in to the tbody tag.
This might shed a little more light on the subject, too: https://github.com/derickbailey/backbone.marionette/wiki/Use-cases-for-the-different-views

Resources