0
var RowView = Backbone.View.extend({
    tagName: 'li',
    className: 'course-row',
    events: {
        "click .table-row": "rowClick" // problem here!
    },
    initialize: function() {},
    template: _.template(tpl),
    render: function() {

        var rowStr = this.template({course: this.model});
        $(this.el).append(rowStr);
    },
    rowClick: function (e) {
        alert(e);
    }
});

Above code defines a Backbone Row view. I'd like to create and render several several rows in a table, so I did:

data.forEach(function(rowData){
    var row = new RowView({model: course, el: mountPoint});
    row.render();
});

If I create 2 rows, the event is bind twice( 2 alert msg if I click on the row), 3 rows 3 times. I avoided this problem by defining the event at parent views, but if I really need attach event handler at each row, how do I avoid the double event biding problem?

Nicolas S.Xu
  • 13,794
  • 31
  • 84
  • 129
  • 1
    There's some strategies for parent / child views in [this answer](http://stackoverflow.com/a/9502515/3585500). But also don't miss the [pub/sub answer](http://stackoverflow.com/a/9415369/3585500) further down. – ourmandave Feb 05 '16 at 19:45
  • This shouldn't be happening. Can you create a [mcve]..? – T J Feb 06 '16 at 04:47
  • The presence of `tagName` and `className` in your `RowView` suggests that `RowView` creates its own `el`. But in that case, a simple `row.render()` wouldn't get anything on the page. Also, the double events suggest that you're binding multiple views to the same `el`. Those things suggest that that's not your real `forEach` loop. What am I missing? – mu is too short Feb 06 '16 at 06:18
  • @muistooshort I use "new RowView({model: course, el: mountPoint});" to create a view, so when rendering, row will render itself on the page. I just updated my question to address your question. Thanks! – Nicolas S.Xu Feb 07 '16 at 17:27
  • 1
    @TJ: Just the old "I'm using one `el` for multiple views" problem all over again, that explains 90% of the "one event being trigger multiple times" bugs with Backbone. – mu is too short Feb 08 '16 at 02:27

1 Answers1

2

You're doing this:

data.forEach(function(rowData){
    var row = new RowView({model: course, el: mountPoint});
    row.render();
});

That means that your view will ignore its tagName and className and just go ahead and bind itself to the el you supply because:

this.el can be resolved from a DOM selector string or an Element; otherwise it will be created from the view's tagName, className, id and attributes properties.

A view's events are bound to its el using delegation and you're binding multiple views (which happen to be the same view "class") so of course you're getting multiple event bindings.

If your rows really are supposed to be <li class="course-row"> elements then you want to do a couple things:

  1. Let the RowViews create and own their own els. Keep the tagName and className attributes in your RowView and stop passing the el to the constructor. You should change $(this.el) to this.$el too, there's no need to create another jQuery object when Backbone has already stashed one away for you.

  2. Return this from RowView's render, this is standard practice and makes the next step a bit nicer.

  3. Whatever creates your RowViews should create them, render them, and then put them inside a container. You appear to be trying to use mountPoint (which is presumable a <ul>) as the container so you want to say:

    data.forEach(function(rowData) {
        var row = new RowView({model: course});
        mountPoint.append(row.render().el);
        // This is why (2) above -----^^^
    });
    

    That assumes that mountPoint is a jQuery instance, if that's not the case then you'd want to say $(mountPoint).append(row.render().el) instead (or do a var $mountPoint = $(mountPoint) above the forEach and use $mountPoint.append inside it).

A personal rule of thumb: if you're passing el to a view constructor then you're almost certainly making a mistake. If you know exactly why you're passing el to the constructor, know the consequences, and know how to deal with them then you're adult enough to run with scissors so go right ahead.

mu is too short
  • 426,620
  • 70
  • 833
  • 800