I have two views, ListView and ItemView. ItemView is populated like this on render in ListView:
this.items.each(function(model, index) {
new ItemView({
item: model,
});
});
All ItemView share the same container el; being a <tbody>, with its template being the <tr>.
When triggering events, they point to the containing element, causing e.g. save to trigger save on all ItemView rather than just the one that was clicked. Is there a way of circumventing this behavior, or is there a better practice?
you can try this one,
ListView = Backbone.View.extend({
events : {
'click tr' : function(e){
this.items.each(function(model){
model.trigger('save');
});
}
}
})
then bind save events in item view.
this.model.on('save', function(){
//your code here
});
Related
This is the code:
NewEntry_CategoryView = Backbone.Marionette.ItemView.extend({
template: "#NewEntry_Category-template",
tagName: "p",
initialize: function () {
$("#sliderContainer").slider();
}
});
NewEntry_CategoriesView = Backbone.Marionette.CompositeView.extend({
template: "#NewEntry_Categories-template",
tagName: "div",
itemView: NewEntry_CategoryView,
itemViewContainer: '#categoryContainer',
appendHtml: function (collectionView, itemView) {
collectionView.$("#categoryContainer").append(itemView.el);
}
});
Why does the jquery ui slider not render when I show the NewEntry_CategoriesView ?
DOM events/manipulation like slide() won't have any effect on the view object's initialization because there is no such DOM element available yet.
Instead, you need to listen to dom:refresh of the view to manipulate its DOM element.
So, just put the code in onDomRefreshin your ItemView
onDomRefresh: function(){ $('#sliderContainer').slide() };
This above is a direct fix. But there are two more things to improve:
Don't call other div outside of this view when possible. In this case, if #sliderContainer belongs to another view, send an event to allow it slide itself. This is not the job of CategoryView. If it is inside current view, refer it with this.$el.find(".some-div") or better yet ui object.
Your collectionView's appendHtml is unnecessary. Marionette also takes of this common case.
I'm new with Backbone and I'm making an example app in which I have to include tabs. The thing is that I have a collection of cities and I want to create one tab for each city (the collection fetchs from the server). I made a view called TabsView, which in the render function passes the collection to a template, and this one loops through the collection and renders the tabs.
What I want to do is that the first tab appears as 'active'. What I've done for the moment is that each tab has a href to a route in the router which changes it's class to active using jquery. Don't know if this is the best way to do this but it works. Maybe there's a better way. Also, when the user clicks a tab, I want to be able to render other view.
Hope I made myself clear. Thanks, cheers,
Martin
Ok I solved this problem doing something like the following:
var Tabs = Backbone.View.extend({
template: JST['tabs'],
events: {
'click li' : 'switchTab'
},
tagName: 'ul',
className: 'nav-tabs',
render: function() {
this.renderTabs();
return this;
},
renderTabs: function() {
this.$el.html(this.template({ cities: this.cities }));
this.$('li:first').addClass('active');
},
switchTab: function(event) {
var selectedTab = event.currentTarget;
this.$('li.active').removeClass('active');
this.$(selectedTab).addClass('active');
}
});
It works fine, maybe it can be improved.
I've been porting my app to use jqMobi and jqUI, but I've run into a problem with backbone delegating events.
The way jqUI creates a side nav bar is umm.... interesting to say the least.
Each panel can have a distinct nav bar, but the nav bar is never actually visible to the user, you populate the nav bar, and then jqUI copies the html into the div#menu element.
My view is fairly straightforward
MyApp.Views.UserMenu = Backbone.View.extend({
el: 'nav#user_menu',
initialize: function(){
//empty out and unbind in-case it is already populated
$(this.el).empty().unbind();
this.render();
},
events: {
"click div#add_friend": "new_friend"
},
render: function(){
$(this.el).append(HandlebarsTemplates['friends/new_friend']());
// here I am trying to change the 'el' to point to where the menu is in the DOM
this.el = 'div#menu';
this.delegateEvents();
return this;
},
new_friend: function(){
alert('clicked');
}
});
I've tried changing the el to the div#menu after populating the nav, but that isn't working. I've also tried populating the div#menu directly, but that doesn't seem to work either.
Any suggestions? I'm assuming the issue is that the elements are being moved, but it could be something else, and maybe I'm not sure how to debug the other case.
Take a HTML tabbar as example. Usually you have a ul and a list of div's. All the Backbone examples that I have found, link the View with only one node by the 'el', 'tagName', etc...
HTML TabBar:
<div class=".tabbar">
<ul class=".tabbar-header">
<li>Cars</li>
<li>Houses</li>
</ul>
<div id="tab-cars" class=".tabbar-item">...</div>
<div id="tab-houses" class=".tabbar-item">...</div>
</div>
Backbone Code:
window.TabBarView = Backbone.View.extend({
el: ???,
tabs: [],
render:function (eventName) {
// Render all tabs in this.tabs
_.each(this.tabs, function (item, position) {
// Render each tab with item.render()
}, this);
return this;
}
});
window.TabBarItemView = Backbone.View.extend({
el: ???,
initialize:function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render:function (eventName) {
// Render the tab header and tab content
return this;
}
});
I wish to add several TabBarItemView's to the TabBarView and each one creates itself the li node inside the ul.tabbar-header and the div.tabbar-item as content.
I've written an article that addresses this issue: http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
It will show you how you can either use a single view to do what you want, or a parent/child setup with a collection view and item view like you're showing in your sample code
you can go as far as to make a separate navigation view, and have the navigation add an item through the render method of your tab-item-view.
when you render the tab item view, you do something like navigation.add(new nav item);
and also add a way to remove the navigation item.
or you can keep the navigation in pure html and append a <li> item with jquery / javascript when you are rendering a tab below.
can't give you a fully working example though, if you really need it i can probably make one tonight,.
I am trying to bind two click events to a single HTML element in two different views. One of the views triggers the event, the other does not.
One of the view has body as its el attribute. If I change this view's el to the same element as the other view's, then both events get triggered.
Is this expected? How can I bind click events for the same element in two different views?
Yes, this is expected. Backbone uses jQuery delegates for the event binding. Which means, the event is actually bound to the view's EL, not directly to the child node.
When you say, "the same element", do you mean literally the exact same node in the DOM? Or, do you mean a node with the same selector? I guess I'm not entirely clear.
can i ask why you want to have 2 views binding to the same element?
from my point of view, you should only have 1 view that represents the element itself
and event's bound to an element should be defined in that view only.
you will run into trouble when you are binding click events to elements that don't belong to the view
if you bind trough the delegateEvents hash, these events are contained within the el of the view.
if you are however defining the click yourself, your code becomes less managable.
so, on to what you can do:
events!
you can define 1 view, holding your button and trigger an event when the button is clicked, while other views that need to handle some code when that button is pressed don't bind directly to the button click itself, they can listen to that raised event.
example on jsfiddle:
http://jsfiddle.net/saelfaer/Qck5w/2/
the gist of it in code here:
// an event aggregator object to trigger and bind to
var events = _.extend({}, Backbone.Events),
// two views that talk to each other trough the event aggregator
var myButtonView = Backbone.View.extend({
// first view binds a click event to the button
events: {
"click a" : "myClickEvent"
},
initialize: function(){
_.bindAll(this, "render");
this.render();
},
render: function(){
return this;
},
// click event executes this function, which triggers a custom event on the events object.
myClickEvent: function(e){
$(e.target).blur();
events.trigger("sidebar:myCustomClickEvent");
return false;
}
});
var myPanelView = Backbone.View.extend({
// second view binds to that event, and executes the custom click handler
initialize: function(){
_.bindAll(this, "render", "myClickEventHandler");
events.bind("sidebar:myCustomClickEvent", this.myClickEventHandler);
this.render();
},
render: function(){
return this;
},
// the click handler does some logic (appends div) when the event is raised.
myClickEventHandler: function(){
var txt = $('<div/>').text("you just clicked the button. (bound in other view)");
$(this.el).append(txt);
}
});