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?

5
  • 1
    There's some strategies for parent / child views in this answer. But also don't miss the pub/sub answer further down. Commented Feb 5, 2016 at 19:45
  • This shouldn't be happening. Can you create a minimal reproducible example..? Commented Feb 6, 2016 at 4: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? Commented Feb 6, 2016 at 6: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! Commented Feb 7, 2016 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. Commented Feb 8, 2016 at 2:27

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.