嵌套集合中的模型更改事件未按预期触发

时间:2011-01-01 22:38:54

标签: javascript backbone.js

我正在尝试在我的第一个“真正的”应用程序中使用backbone.js,我需要一些帮助来调试为什么某些模型更改事件没有像我期望的那样触发。

如果我从服务器创建一个来自JSON 数组的集合,那么按设定的时间间隔获取()它,如果集合中的单个模型发生了变化,我不会收到通知。主干文档建议应生成此类通知。我似乎得到的只是每次获取的刷新通知,这是没用的。 OTOH,如果我从服务器创建一个来自JSON 对象的模型,然后按设定的时间间隔获取()模型,我会在属性发生变化时收到更改通知。有什么想法吗?

详情

/ employees / {username} / tasks中的我的Web服务返回一个JSON任务对象数组,每个任务对象都嵌套一个子任务对象的JSON数组。例如,

[{
  "id":45002,
  "name":"Open Dining Room",
  "subtasks":[
    {"id":1,"status":"YELLOW","name":"Clean all tables"},
    {"id":2,"status":"RED","name":"Clean main floor"},
    {"id":3,"status":"RED","name":"Stock condiments"},
    {"id":4,"status":"YELLOW","name":"Check / replenish trays"}
  ]
},{
  "id":47003,
  "name":"Open Registers",
  "subtasks":[
    {"id":1,"status":"YELLOW","name":"Turn on all terminals"},
    {"id":2,"status":"YELLOW","name":"Balance out cash trays"},
    {"id":3,"status":"YELLOW","name":"Check in promo codes"},
    {"id":4,"status":"YELLOW","name":"Check register promo placards"}
  ]
}]

另一个Web服务允许我更改特定任务中特定子任务的状态,如下所示:/ tasks / 45002 / subtasks / 1 / status / red [旁边 - 我打算将此更改为HTTP Post基于服务,但当前的实现更容易调试]

我的JS应用程序中有以下类:

子任务模型和子任务集合

var Subtask = Backbone.Model.extend({});

var SubtaskCollection = Backbone.Collection.extend({
  model: Subtask
});

具有子任务集合的嵌套实例的任务模型

var Task = Backbone.Model.extend({
  initialize: function() {

    // each Task has a reference to a collection of Subtasks
    this.subtasks = new SubtaskCollection(this.get("subtasks"));

    // status of each Task is based on the status of its Subtasks
    this.update_status();

  },
  ...
});

var TaskCollection = Backbone.Collection.extend({
  model: Task 
});

任务视图,用于呈现项目并侦听模型的更改事件

var TaskView = Backbone.View.extend({

  tagName:  "li",

  template: $("#TaskTemplate").template(),

  initialize: function() {
    _.bindAll(this, "on_change", "render");
    this.model.bind("change", this.on_change);
  },

  ...

  on_change: function(e) {
    alert("task model changed!");
  }

});

当应用程序启动时,我实例化一个TaskCollection(使用上面列出的第一个Web服务中的数据),将一个更改事件的侦听器绑定到TaskCollection,并设置一个重复的setTimeout来获取()TaskCollection实例。

...

TASKS = new TaskCollection();

TASKS.url = ".../employees/" + username + "/tasks"

TASKS.fetch({
  success: function() {
    APP.renderViews();
  }
});


TASKS.bind("change", function() {
  alert("collection changed!");
  APP.renderViews();
});


// Poll every 5 seconds to keep the models up-to-date.
setInterval(function() {
  TASKS.fetch();  
}, 5000);

...

第一次,所有东西都按预期呈现。但是在这一点上,如果我使用第二个Web服务更改子任务的状态,我会期望(或两者)Collection更改事件或模型更改事件被触发,但这不会发生。

有趣的是,如果我添加了一个额外的级别的嵌套,并且Web服务返回单个模型,我确实会触发更改事件,例如:

"employee":"pkaushik", "tasks":[{"id":45002,"subtasks":[{"id":1.....

但这似乎很糟糕......而且我担心我没有正确构建我的应用程序。如果它有帮助,我会包含更多代码,但这个问题已经相当冗长。

思想?

1 个答案:

答案 0 :(得分:8)

您正在侦听任务中的事件,而不是子任务。如果您希望您的任务也为子任务承载事件,您应该在Task.initialize中执行类似的操作:

var self = this;    
this.subtasks.bind("refresh", function(){ self.trigger("refresh:subtask")});
this.subtasks.each(function(st){ 
  st.bind("change", function(){ self.trigger("change:subtask:" + st.id, st) });
});

您可能还想在刷新事件后刷新绑定。这样,当子任务模型上发生更改事件时,任务模型也将触发更改模型。

Backbone仅在获取后发送刷新事件,您将不会被告知特定模型的更改。查看集合的fetch函数:

options.success = function(resp) {
  collection[options.add ? 'add' : 'refresh'](collection.parse(resp), options);
  if (success) success(collection, resp);
};

默认情况下会调用refresh。这是刷新功能:

refresh : function(models, options) {
  models  || (models = []);
  options || (options = {});
  this.each(this._removeReference);
  this._reset();
  this.add(models, {silent: true});
  if (!options.silent) this.trigger('refresh', this, options);
  return this;
}

它将静默地将所有模型添加到集合中(没有事件),然后触发刷新事件,但它不会识别已更改的元素。