嵌套骨干模型在保存时会产生无限递归

时间:2013-11-08 03:37:50

标签: backbone.js

当我更新到Backbone 1.1时,似乎出现了这个问题。我有一个嵌套的Backbone模型:

var ProblemSet = Backbone.Model.extend({
    defaults: {
        name: "",
        open_date: "",
        due_date: ""},
    parse: function (response) {
        response.name = response.set_id;
        response.problems = new ProblemList(response.problems);
        return response; 
    }
});

var ProblemList = Backbone.Collection.extend({
     model: Problem
});

我最初加载了一个ProblemSetList,它是我页面中ProblemSet模型的集合。对任何ProblemSet的open_date或due_date字段的任何更改,首先转到服务器并更新该属性,然后返回。这会在ProblemSet上触发另一个更改事件。

似乎服务器的所有后续返回都会触发另一个更改事件,更改的属性是“problems”属性。这导致无限递归调用。

问题似乎来自Backbone.Model的set方法部分(此处列出的第339行代码)

// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
    val = attrs[attr];
    if (!_.isEqual(current[attr], val)) changes.push(attr);
    if (!_.isEqual(prev[attr], val)) {
      this.changed[attr] = val;
    } else {
      delete this.changed[attr];
    }
    unset ? delete current[attr] : current[attr] = val;
  }

  // Trigger all relevant attribute changes.
  if (!silent) {
    if (changes.length) this._pending = true;
    for (var i = 0, l = changes.length; i < l; i++) {
      this.trigger('change:' + changes[i], this, current[changes[i]], options);
    }
  }

Problems属性的比较从_.isEqual()返回false,因此触发change事件。

我的问题是:这是做嵌套Backbone模型的正确方法吗?我在Backbone 1.1中有类似的工作。关于如何避免这个问题的其他想法?

2 个答案:

答案 0 :(得分:0)

在获取和保存时调用parse(根据主干文档),这可能会导致无限循环。我不认为解析函数是创建新的ProblemsList子集合的正确位置,而是在模型的初始化函数中执行。

答案 1 :(得分:0)

每次problems完成时,您都会重新验证model.fetch属性,对象不同,从而触发新的循环。

我通常用来处理嵌套模型:

  • 使用Backbone处理的属性之外的模型属性
  • initialize函数
  • 中实例化它 在父set函数中
  • resetparse此对象并返回省略设置数据的响应

这样的事情:

var ProblemSet = Backbone.Model.extend({
    defaults: {
        name: "",
        open_date: "",
        due_date: ""
    },
    initialize: function (opts) {
        var pbs = (opts && opts.problems) ? opts.problems : [];
        this.problems = new ProblemList(pbs);
    },
    parse: function (response) {
        response.name = response.set_id;
        if (response.problems)
            this.problems.set(response.problems);
        return _.omit(response, 'problems');
    }
});