Events Wont work on Backbone.js - javascript

I created a view and has the ff codes:
var app = app || {};
app.singleFlowerView = Backbone.View.extend({
tagName: 'article',
className: 'flowerListItem',
// tells where to apply the views
template: _.template( $("#flowerElement").html() ),
// render
render: function(){
var flowerTemplate = this.template(this.model.toJSON());
// el contains all the prop above and pass to backbone
this.$el.html(flowerTemplate);
return this;
},
events: {
'mouseover': 'addBgColor',
'mouseout': 'removeBgColor'
},
addBgColor: function(){
this.$el.addBgColor('bgColorImage');
},
removeBgColor: function(){
this.$el.removeBgColor('bgColorImage');
}
});
When I run this to my HTML file I got the error addBgColor and removeBgColor is not a function. I have the CSS for this and all the models and views were set up.
Am I missing something here? Any idea why events doesn't work?

this.$el.addBgColor is the problem.
The events are triggering but you're calling addBgColor on the $el jQuery object, which is not a jQuery function, like the error message is telling you.
Check what's the difference between $el and el.

Tony, your events are cool and they are running they're just not doing anything.
this.addBgColor() will call your function in a view.
this.$el is referring to the html and there's no property called addBgColor assigned to $el.
You need to do something like change the class on your tag with the functions like so...
addBgColor: function(){
this.$el.className = 'bgColorImage'
},
.bgColorImage {
background-image: url('bgColorImage.jpg');
}

Related

this.$el.off is not a function

Previously this will work but I've update the underscore and backbone to the latest version, then I got error of
Uncaught TypeError: this.$el.off is not a function
http://jsfiddle.net/mmm770v8/
SearchView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template( $("#search_template").html(), {} );
this.el.html( template );
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(){
// Button clicked
console.log(this.el.find('#search_input').val());
}
});
You have several problems:
Your fiddle was using jQuery 1.5.2 which is ancient and used bind/unbind instead of on/off. Backbone expects a more recent version of jQuery which has on and off functions.
You're using this.el where you mean this.$el. this.el is just a plain old DOM node, this.$el is the cached $(this.el).
The var html = _.template(tmpl, data) form of _.template went away in Underscore 1.7.0. You now need a two step process:
var t = _.template(tmpl);
var h = t(data);
so your render should look more like this:
render: function() {
var template = _.template($("#search_template").html());
this.$el.html(template({}));
}
Updated fiddle: http://jsfiddle.net/ambiguous/L5z4agh4/

Child object calls siblings' methods

I have the following structure:
var PopupView = Backbone.View.extend({
el:'#shade',
events:{
'click .popup-cancel': 'hide',
'click .popup-commit': 'commit',
},
show: function(){
...
},
hide: function(){
...
},
initialize: function(){
_.bindAll(this, 'show','hide','commit');
}
});
var Popup1 = PopupView.extend({
render: function(){
...
},
commit: function(){
console.log('1');
...
},
});
var Popup2 = PopupView.extend({
render: function(){
...
},
commit: function(){
console.log('2');
...
},
});
The problem is that when I click .popup-commit from one of the popups, it actually triggers the methods of both of them. I've tried moving the declaration of events and initialize() up into the child classes, but that doesn't work.
What's going on, and how can I fix it (so that the commit method of only the view I'm triggering it on gets fired)?
Your problem is right here:
el:'#shade'
in your PopupView definition. That means that every single instance of PopupView or its subclasses (except of course those that provide their own el) will be bound to the same DOM node and they will all be listening to events on on id="shade" element.
You need to give each view its own el. I'd recommend against ever setting el in a view definition like that. I think you'll have a better time if you let each view create (and destroy) its own el. If you do something like:
var PopupView = Backbone.View.extend({
className: 'whatever-css-class-you-need',
tagName: 'div', // or whatever you're using to hold your popups.
attributes: { /* whatever extra attributes you need on your el */ },
//...
});
then your views will each get their own el. See the Backbone.View documentation for more information on these properties.

backbone and $el element

I'm trying to develop my first backbone application. All seems ok, but when i render the view and append some html to the $el, nothing is rendered in the page.
Rest service calls done ok, the Backbone.Router.extend is declared inside $(document).ready(function () {}); to ensure that the DOM is created.
Debugging my javascript, the el element get to contain the correct value in the innerHTML property, but when the whole page is rendered, this value doesn't appear in the page.
¿What am i doing wrong?
My View code:
window.ProductsListView = Backbone.View.extend({
id: 'tblProducts',
tagName: 'div',
initialize: function (options) {
this.model.on('reset', this.render, this);
},
render: function () {
// save a reference to the view object
var self = this;
// instantiate and render children
this.model.each(function (item) {
var itemView = new ProductListItemView({ model: item });
var elValue = itemView.render().el;
self.$el.append(elValue); // Here: the $el innerHTML is ok, but in the page it disappear. The id of element is div#tblProducts, so the element seems correct
});
return this;
}
});
window.ProductListItemView = Backbone.View.extend({
tagName: 'div',
template: _.template(
'<%= title %>'
),
initialize: function (options) {
this.model.on('change', this.render, this);
this.model.on('reset', this.render, this);
this.model.on('destroy', this.close, this);
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
// $(this.el).html('aaaaaa'); // This neither works: it's not a template problem
return this;
},
close: function () {
$(this.el).unbind();
$(this.el).remove();
}
});
Here i load products (inside Backbone.Router.extend). This is executed correctly:
this.productsList = new ProductsCollection();
this.productsListView = new ProductsListView({ model: this.productsList });
this.productsList.fetch();
And this is the html element i want to render:
<div id="tblProducts">
</div>
Thanks in advance,
From the code you have posted, you are not actually inserting your ProductsListView in to the DOM or attaching it to an existing DOM element.
The way I like to look at it is you have two types of Views:
Those that are dynamically generated based on data returned from the server
Those that already exist on the page
Usually in the case of lists, the list already exists on the page and it's items are dynamically added. I have taken your code and restructured it slightly in this jsfiddle. You will see that the ProductListView is binding to an existing ul, and ProductItemView's are dynamically appended when they are added to the Collection.
Updated jsfiddle to demonstrate Collection.reset
The el property exists within the view if it is rendered or not. You can't say it is ok there because Backbone will create an element if no element is passed (empty div).
If you want to render the view you should determine what is the container of the element? Do you have an html you want to attach the view to?
Try passing a container element by calling the view with an el like
this.productsListView = new ProductsListView({ model: this.productsList, el : $("#container") });
Of course you can create the view and attach it to the DOM later:
el: $("#someElementID") //grab an existing element
el.append(view.render().el);
Your view wont exist in the dom until you attach it somewhere.

View Events not firing on created elements?

Trying to create a todo example app to mess around with backbone. I cannot figure out why the click event for the checkbox of a task is not firing. Here is my code for the TaskCollection, TaskView, and TaskListView:
$(document).ready(function() {
Task = Backbone.Model.extend({});
TaskCollection = Backbone.Collection.extend({
model: 'Task'
});
TaskView = Backbone.View.extend({
tagName: "li",
className: "task",
template: $("#task-template").html(),
initialize: function(options) {
if(options.model) {
this.model = options.model
}
this.model.bind('change',this.render,this);
this.render();
},
events: {
"click .task-complete" : "toggleComplete"
},
render: function(){
model_data = this.model.toJSON();
return $(_.template(this.template, model_data));
},
toggleComplete: function() {
//not calling this function
console.log("toggling task completeness");
}
});
TaskListView = Backbone.View.extend({
el: $("#task-list"),
task_views: [],
initialize: function(options) {
task_collection.bind('add',this.addTask,this);
},
addTask: function(task){
task_li = new TaskView({'model' : task});
this.el.append(task_li.render());
this.task_views.push(task_li);
},
});
});
Template for the task:
<script type='text/template' id='task-template'>
<li class="task">
<input type='checkbox' title='mark complete' class='task-check' />
<span class='task-name'><%= name %></span>
</li>
</script>
I can't seem to figure out why the toggleComplete event will not fire for the tasks. how can I fix this?
The problem here is that the backbone events only set to the element of the view (this.el) when you create a new view. But in your case the element isn't used. So you have the tagName:li attribute in your view, which let backbone create a new li element, but you doesn't use it. All you return is a new list element created from your template but not the element backbone is creating, which you can access by this.el
So you have to add your events manually to your element created by your template using jQuery or add your template as innerHtml to your element:
(this.el.html($(_.template(this.template, model_data)))
Try changing the lines where you set your listeners using .bind() to use .live(). The important difference is .live() should be used when you want to bind listeners to elements that will be created after page load.
The newest version of jQuery does away with this bit of ugliness and simplifies the methods used to set event listeners.
Your event is binding to a class of .task-complete but the class on your checkbox is .task-check
Try modifying your render function to call delegateEvents() like so:
render: function(){
model_data = this.model.toJSON();
this.el = $(_.template(this.template, model_data));
this.delegateEvents();
return this.el;
},
You'd really be better off changing your template to not include the li and then return this.el instead of replacing it, but if you want the events to work you need to have this.el be the root element one way or another; delegateEvents() re-attaches the event stuff, so when you change this.el that should fix the issue.
#Andreas Köberle answers it correctly. You need to assign something to this.elto make events work.
I changed your template and your TaskView#render() function.
This JSFiddle has the changes applied.
New render function:
render: function(){
var model_data = this.model.toJSON();
var rendered_data = _.template(this.template, model_data);
$(this.el).html(rendered_data);
return this;
}
It is recommended that the render() returns this.
One line in your TaskListView#addTask function changes from this.el.append(task_li.render()); to this.el.append(task_li.render().el);.
Template change
Since we are using this.el in the render() function, we have to remove the <li> tag from the template.
<script type='text/template' id='task-template'>
<input type='checkbox' title='mark complete' class='task-complete' />
<span class='task-name'><%= name %></span>
</script>

Backbone and jQuery events

I have a simple task - retrieve click listener function from DOM element.
I've fased two problems:
I have no idea how to obtain event listener, that was set via addEventListener function
$(element).data('events') is always empty
Talking about first problem - I think it's not critical as I'm using this function only in one place. But the second problem is a huge pain...
I've tested on pure jQuery environment:
$(element).click(function(){})
$(element).data('events') /*contains events info*/
But with Backbone:
$(element).click(function(){})
$(element).data('events') /*alway empty*/
I'm not a JS guru but it seems like there no bound data at all... Maybe it's just a typical Backbone behaviour, but still - how can I retrieve event handler?
If you are using Backbone.js you should be managing your events inside a Backbone.View object and avoid capturing the event with JQuery directly.
You should try something like this:
var myBody = $( 'body' );
var myDIV = $( '<DIV id="contentDIV"></DIV>' );
myBody.append( myDIV );
var myButton = $( '<button id="aButton">test</button>' );
myDIV.append ( myButton );
var MyView = Backbone.View.extend({
el : myDIV,
events: { 'click button#aButton' : 'doSomething' }, //here you bound the
//event to a function.
initialize: function(){
_.bindAll(this, 'render')
},
render: function(){
myDIV.append('<br>working...');
},
doSomething: function(){
alert( 'doSomething function.. after click event' );
}
});
var myView = new MyView;
myView.render();
PS: A good tutorial for understanding how it works: http://arturadib.com/hello-backbonejs/

Categories