简体   繁体   中英

Strange behavior with “this” in Backbone.js when binding to “add”/“remove” in a collection

I have the following two views based on Backbone.js

pg.views.ItemList = Backbone.View.extend({

  tagName:  "div",
  className: "items",

  initialize: function() {
    _.bindAll(this, 'addOne', 'addSelected')

    Items.bind('add',     this.addOne);

    Items.fetch();
  },

  // REMOVED

  addOne: function(Item) {
    console.log($(this.el));
    var view = new pg.views.Item({model: Item});
    $(this.el).append(view.render().el);
  },

  addSelected: function(ItemList) {
    _.each(ItemList, this.addOne);
    return this.el;
  },

  // REMOVED

});

pg.views.Section = Backbone.View.extend({

  tagName:  "section",

  sectionTemplate: _.template($('#section-template').html()),

  events: {
    // REMOVED
  },

  initialize: function() {
    _.bindAll(this, 'render', 'close', 'addItemToSection', 'removeItemFromSection');
    this.model.bind('change', this.render);
    this.model.view = this;

    Items = new pg.collections.ItemList;
  },

  render: function() {
    $(this.el).html(this.sectionTemplate(this.model.toJSON()));
    this.setContent();
    Items.bind('add', this.addItemToSection);                   // "add" event bind
    Items.bind('remove', this.removeItemFromSection);           // "remove" event bind
    this.addItems();
    return this;
  },

  addItems: function() {
    var ids = this.model.get('items');
    var view = new pg.views.ItemList;
    var items = _.map(ids, function(id){ return Items.get(id); });
    $(this.el).append(view.addSelected(items));
  },

  // FUNCTIONS REMOVED

  setContent: function() {
    var title = this.model.get('title');
    this.$('.title').text(title);
    this.title = this.$('.title-input');
    this.title.val(title);
  },

  addItemToSection: function(Item) {
    console.log(this.model.get('title'));
    // REMOVED
  },

  removeItemFromSection: function(Item) {
    console.log(this.model.get('title'));
    // REMOVED
  }

});

Here is the problem I am encountering.

I have a view where a user creates two sections, lets say that they are called "section1" and "section2". These names are used in their "title" attribute.

Here is the strange behavior I am observing:

  1. When a user is in "section1" and adds and item, the "add" bind event is triggered, this results in "section2" being written to the console.

  2. When a user is in "section1" and adds and item, the "remove" bind event is triggered, this results in "section1" being written to the console.

  3. When a user is in "section2" and adds and item, the "add" bind event is triggered, this results in "section2" being written to the console.

  4. When a user is in "section2" and adds and item, the "remove" bind event is triggered, this results in "section2" and then "section1" being written to the console.

If I am binding "add" inside the "pg.views.Section" view using "this.addItemToSection", shouldn't it only call the "addItemToSection" block inside that instance?

The only line that I can see "redefining" the "this" is the "add" bind in the initialize block of "pg.views.ItemList". If that line is the culprit, how do I prevent it from redefining the "this" on the "add" bind for "section1"?

I think this is a global problem, pun intended ;) You are calling everything on Items which firstly has been declared with var - (bad practice) implies it'll be bound to the window object. And you use the same variable reference in an another view! So both will get call because they refer to the same object!!!! You can have as many 'local' instances you want by binding to 'this'. Try the following changes, I guess they should work.

pg.views.ItemList = Backbone.View.extend({

  tagName:  "div",
  className: "items",

  initialize: function() {
    _.bindAll(this, 'addOne', 'addSelected')

    this.myItems = new pg.collections.ItemList(); //create local reference

    this.myItems.bind('add', this.addOne);

    this.myItems.fetch();
  },

  ...    
});

pg.views.Section = Backbone.View.extend({

  tagName:  "section",


  initialize: function() {
    _.bindAll(this, 'render', 'close', 'addItemToSection', 'removeItemFromSection');
    this.model.bind('change', this.render);
    this.model.view = this;

    this.mySectionItems = new pg.collections.ItemList; //add to 'this'
  },

  render: function() {
    $(this.el).html(this.sectionTemplate(this.model.toJSON()));
    this.setContent();
    this.mySectionItems.bind('add', this.addItemToSection);         // "add" event bind
    this.mySectionItems.bind('remove', this.removeItemFromSection); // "remove" event bind
    this.addItems();
    return this;
  },
...

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM