GSto
GSto

Reputation: 42370

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?

Upvotes: 1

Views: 385

Answers (5)

Andreas K&#246;berle
Andreas K&#246;berle

Reputation: 111032

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)))

Upvotes: 2

Wieczo
Wieczo

Reputation: 465

@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>

Upvotes: 0

taxilian
taxilian

Reputation: 14324

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.

Upvotes: 0

blockhead
blockhead

Reputation: 9705

Your event is binding to a class of .task-complete but the class on your checkbox is .task-check

Upvotes: 0

mattacular
mattacular

Reputation: 1889

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.

Upvotes: 0

Related Questions