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><scripttype='text/javascript'src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script><scripttype='text/javascript'src='http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js'></script><scripttype='text/javascript'src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js'></script><scripttype='text/javascript'>
      $(function() {
        //initialize the contacts collection and add some contacts
        contacts = newContacts();
        contacts.add(newContact());
        contacts.add(newContact());

        //only need to render the ContactsView oncevar view = newContactsView({ 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(newContact());
        });
      });

      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 rowtagName: 'tr',

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

      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 tabletagName: 'table',

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

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

          returnthis;
        },

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

    </script><scripttype='text/template'id='contact-row'><td><%= first_name %></td><td><%= last_name %></td><td><%= address %></td></script><scripttype='text/template'id='contacts-table-header'><thead><th>First Name</th><th>Last Name</th><th>Address</th></thead></script></head><body><buttonid="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)
    returnthis; // 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?"