jviotti
jviotti

Reputation: 18949

Event triggered in all instances of Backbone.View

I'm displaying a table of categories with Backbone. I created two views:

The definitions:

RowView = Backbone.View.extend({
  el: "#content table tbody",
  initialize: function() {
    this.render();
  },
  render: function(){
    var params = { name: this.model.get('name'), route: this.options.route };
    var template = _.template( $("#rowTemplate").html(), params);
    this.$el.append(template);
  },
  events: {
    "click #name": "clickHandler"
  },
  clickHandler: function( event ) {
    console.log('Browse subcategories of ' + this.model.get('name'));
  }
});

TableView = Backbone.View.extend({
  el: "#content",
  initialize: function(){
    this.render();
  },
  render: function(){
    var row = new this.collection();
    var that = this;
    row.fetch({
      success: function() {
        console.log('Collection fetch succeeded');
        var params = { title: that.options.title,
                       counter: row.at(0).get('counter'),
                       route: that.options.route
                     };

        var template = _.template( $("#tableTemplate").html(), params);
        that.$el.html( template );
        // RowView's are created by iteration here
        for(var x = 1; x < row.length; x++) {
          var params = { model: row.at(x), route: that.options.route };
          var view = new RowView(params);
        }
      }
    });
  }
});

As you can see, I've attached a click event at the RowView.

RowView template:

<script type="text/template" id="rowTemplate">
<tr>
  <td id="name" class="fill"><%= name %></td>
  <td><a href="#<%= route %>/<%= name %>/edit" class="btn">Editar</a></td>
</tr>
</script>

Clicking any #name triggers the handler in all instance of the view. So when clicking one category I get:

Browse subcategories of category1 127.0.0.1:68
Browse subcategories of category2 127.0.0.1:68
etc...

As far as I know, that's because all RowView's are delegated to the same el.

The first thing I though about was adding the category name to the rowTemplate and compare the value in the DOM with the value in the view to see which one actually triggers the event.

But that solutions look really ugly. What's the correct way of accomplishing this in Backbone?

EXTRA: Is it considered better if I only create one view, and iterate in the template to generate the rows?

EDIT: I think the provided code is enough. Otherwise I can add them.

Upvotes: 0

Views: 885

Answers (3)

Venkat Kotra
Venkat Kotra

Reputation: 10753

Try this

RowView = Backbone.View.extend({
    container: '#content table tbody',
    tagName: 'tr',


  //  initialize: function() {
 //           this.render();
 //       },
    render: function() {
        var params = {
            name: this.model.get('name'),
            route: this.options.route
        };
        var template = _.template($("#rowTemplate").html(), params);
        this.$el.append(this.template);
    },
    events: {
        "click .name": "clickHandler"
    },
    clickHandler: function(event) {
        console.log('Browse subcategories of ' + this.model.get('name'));
    }
});

RowView template (no need for identifying each row view):

<script type="text/template" id="rowTemplate">
<td class="name"><%= name %></td>
<td><a href="#<%= route %>/<%= name %>/edit" class="btn">Editar</a></td>
</script>

Then the table view:

...
       that.$el.html( template );
        // RowView's are created by iteration here
        for(var x = 1; x < row.length; x++) {
          var params = { model: row.at(x), route: that.options.route };
          var view = new RowView(params);
          that.$el.find('tbody').append(view.el);
          view.render()

        }
...

Upvotes: 0

dehrg
dehrg

Reputation: 1741

You have more than one element with the same id on your page, due to all of your rows having the

<td id="name" class="fill"> element.

Element IDs should be unique within your document.

One solution would be to distinguish the rows in your template, and use events as a function to set the proper ID.

Template:

<script type="text/template" id="rowTemplate">
<tr>
  <td id="name-<%= name %>" class="fill"><%= name %></td>
  <td><a href="#<%= route %>/<%= name %>/edit" class="btn">Editar</a></td>
</tr>

Events function:

events: function(){
  _events = {};
  _events["click #name-" + this.model.get('name')] = "clickHandler";
  return _events;
}

Upvotes: 0

anhulife
anhulife

Reputation: 566

you can modify RowView like this :

RowView = Backbone.View.extend({
    container: '#content table tbody',
    tagName: 'tr',
    initialize: function() {
        this.render();
    },
    render: function() {
        var params = {
            name: this.model.get('name'),
            route: this.options.route
        };
        var template = _.template($("#rowTemplate").html(), params);
        this.$el.html(template).appendTo(this.container);
    },
    events: {
        "click .fill": "clickHandler"
    },
    clickHandler: function(event) {
        console.log('Browse subcategories of ' + this.model.get('name'));
    }
});

and RowView template:

<script type="text/template" id="rowTemplate">
<td class="fill"><%= name %></td>
<td><a href="#<%= route %>/<%= name %>/edit" class="btn">Editar</a></td>
</script>

Backbone.js will create a tr element. then this.$el.html(template).appendTo(this.container) fill the tr element with template and append to #content table tbody.

just like that, RowView's events be delegated on RowView's el, not #content table tbody.

Upvotes: 1

Related Questions