Backbone.js - Is this button and event bound corre

2019-07-29 05:28发布

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:

enter image description here

I have:

  • A Contact model
  • A Contacts collection
  • A Contacts view (acts as the main view)
  • A ContactRow view

So far, the project is working fine - except for a button that is supposed to trigger the function that adds the row.

Here is my code so far:

$(function($) {

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


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



    window.ContactRow = Backbone.View.extend({
        //el: $("#contacts-table table tbody"),     

        row_template: _.template($("#contact-row").html()),

        initialize: function() {
            _.bindAll(this, "render")
        },

        render: function() {
            $("tbody").html("<tr><td>Look at me, I'm a row!</td></tr>");
            return this;
        }
    }); 


    window.ContactsView = Backbone.View.extend({        
        el: $("#contacts-container"),   

        events: {
            "click button#add_contact": "addContact"
        },

        template: _.template($("#contacts-table").html()),

        initialize: function() {
            _.bindAll(this, "render", "addContact", "appendContact");   

            this.collection = new Contacts();
            this.collection.bind("add", this.appendContact);

            var contactRow = new ContactRow();          
            this.render();
            this.appendContact(); // Just a test to see if the function is working
        },

        render: function() {
            $("#button-container").append("<button id='add_contact'>Add Contact</button>");
            $(this.el).html(this.template);
            _(this.collection.models).each(function(contact) {
                appendContact(contact);
            }, this)            
        },

        addContact: function() {
            console.log("yup, it works!"); // well, not yet
            var contact = new Contact();
            this.collection.add(contact);               

        },

        appendContact: function(contact) {
            var contactRow = new ContactRow({
                model: contact
            });

            $("body").append(contactRow.render().el);
        }

    });

     var contactsView = new ContactsView(); 

}, jQuery);

As you can see, I have an addContact function that is tied to the click event of the "Add Contact" button that is being appended to a div element on the main page during the render process.

I'm attempting to write log messages to the console, but the button doesn't seem to be firing off the method and I can't figure out why.

It's the end of the day and my brain is fried so I'd appreciate any pointers on this. Thanks.

3条回答
对你真心纯属浪费
2楼-- · 2019-07-29 06:10

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?
  ...
查看更多
Melony?
3楼-- · 2019-07-29 06:16

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>
查看更多
可以哭但决不认输i
4楼-- · 2019-07-29 06:26
        _(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);
查看更多
登录 后发表回答