Skip to content Skip to sidebar Skip to footer

Backbone.js - Is This Button And Event Bound Correctly?

I'm experimenting with Backbone.js by creating a table view, and a seperate row view and trying to add the row to the table: I have: A Contact model A Contacts collection A Cont

Solution 1:

Here's a working example. I updated the code with best practices for using Backbone.

Notice I didn't add the button through a Backbone view. The button is part of the html body, and I just subscribe to its click event and then add a contact to the contacts collection.

<html>
  <head>
    <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script>
    <script type='text/javascript' src='http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js'></script>
    <script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js'></script>
    <script type='text/javascript'>
      $(function() {
        //initialize the contacts collection and add some contacts
        contacts = new Contacts();
        contacts.add(new Contact());
        contacts.add(new Contact());

        //only need to render the ContactsView once
        var view = new ContactsView({ collection: contacts });
        $("body").append(view.render().el);

        //adding a contact to the contacts list when the
        //button is clicked
        $("#add-contact").click(function() {
          contacts.add(new Contact());
        });
      });

      Contact = Backbone.Model.extend({
        defaults: {
          first_name: "John",
          last_name: "Smith",
          address: "123 Main St"
        }
      }); 

      Contacts = Backbone.Collection.extend({
        model: Contact
      }); 

      ContactRow = Backbone.View.extend({
        initialize: function() {
          _.bindAll(this, "render");
          this.template = _.template($("#contact-row").html());
        },

        //every backbone view has a tagName. the default tagName is 'div'
        //we're changing it to a table row
        tagName: 'tr',

        render: function() {
          $(this.el).html(this.template(this.model.toJSON()));
          return this;
        }
      }); 

      ContactsView = Backbone.View.extend({        
        initialize: function() {
          _.bindAll(this, "render");
          this.headerTemplate = $("#contacts-table-header").html();
          this.collection.bind("add", this.renderContact, this);
        },

        //the ContactsView element will be a table
        tagName: 'table',

        render: function() {
          $(this.el).html(this.headerTemplate);

          this.collection.each(function(contact) {
            this.renderContact(contact);
          }, this);

          return this;
        },

        renderContact: function(contact) {
          var contactView = new ContactRow({ model: contact });
          $(this.el).append(contactView.render().el);
        }
      });

    </script>

    <script type='text/template' id='contact-row'>
      <td><%= first_name %></td>
      <td><%= last_name %></td>
      <td><%= address %></td>
    </script>

    <script type='text/template' id='contacts-table-header'>
      <thead>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Address</th>
      </thead>
    </script>
  </head>
  <body>
    <button id="add-contact">Add Contact</button>
  </body>
</html>

Solution 2:

This is because you're appending<button id='contact'> after backbone has traversed your event collection.

When you create a backbone view delegateEvents is called behind the scenes. This is where backbone looks at your events hash and wires everything up. To fix Either:

  • append <button id='contact'> before creating the view

    or

  • or manually call backbone's delegateEvents after rendering

So your render function may look like:

render: function() {
    $("#button-container").append("<button id='add_contact'>Add Contact</button>");
    $(this.el).html(this.template);
    this.delegateEvents(); // re-wire events matching selectors in the event hash
    _(this.collection.models).each(function(contact) {
        appendContact(contact);
    }, this)
    return this; // you should also do this so you can chain         
},

Update:

It seems odd that you'd have to manually call delegateEvents. It could also be that #button-contianer isn't a child of the view's el. All of the selectors in that event hash are scoped to el, so if #button-contianer isn't a child of it the button#add_contact selector will never find anything. As a proof of concept, try this: in your render method:

render: function() {
  console.log($(this.el).find("button#add_contact").length) // get anything?
  ...

Solution 3:

        _(this.collection.models).each(function(contact) {
            appendContact(contact);
        }, this) 

This code won't work, because you don't have a variable named appendContact. Should be:

        _(this.collection.models).each(this.appendContact(contact), this);

Post a Comment for "Backbone.js - Is This Button And Event Bound Correctly?"