嵌套在 firebase 集合模型中的集合没有添加功能

The collection nested inside firebase collection's model doesn't have add function

在我的应用程序中,我尝试使用 Firebase 来存储基于 backbone 框架的实时数据。

问题是这样的:
我有一个子级别模型和集合,它们都是通用 backbone 模型和集合。

var Todo = Backbone.Model.extend({
    defaults: { 
        title: "New Todo",
        completed : true
    }
});

var Todocollection = Backbone.Collection.extend({
    model: Todo,
    initialize: function() {
        console.log("creating a todo collection...");
    },
});

然后是高级模型,其中包含子级集合作为属性。

var Daymodel = Backbone.Model.extend({
    defaults : {
        day: 1,
        agenda : new Todocollection()
    }
});

然后对于更高级别的收集,我将使用 firebase 收集

var DayCollection = Backbone.Firebase.Collection.extend({
    model: Daymodel
});

到目前为止,我可以正确地将数据添加到更高级别的集合,它有一个 day 属性和一个 agenda 属性(应该是 TodoCollection)。

问题是当我尝试将数据添加到子级集合时,它无法正常工作。

this.collection.last()
    .get("agenda")
    .add({
        title: this.input.val(), 
        completed: false
    });

以上代码将在视图部分内。 this.collection.last() 将获得最后一个模型。 get("agenda")应该是集合对象。

但是不行。错误显示 this.collection.last(...).get(...).add 不是函数。

调试后发现this.collection.last().get("agenda") returns是一个普通的JS对象,而不是集合对象

我进一步调试如果使用backbone集合作为外层集合DayCollection。一切顺利。

如何解决这样的问题?

为什么默认的 collection 属性不再是 collection?

当您获取或创建一个新的 Daymodel 时,我认为它看起来像这样:

{
    day: 1,
    agenda : [{
        title: "New Todo",
        completed : false
    }, {
        title: "other Todo",
        completed : false
    }]
}

最初是 Todocollection 的默认 agenda 属性被替换为 object 的原始数组。 Backbone 不知道 agenda 是 collection 并且不会自动填充它。

这就是 Backbone 对 defaults at model creation (line 401) 所做的:

var defaults = _.result(this, 'defaults');
attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
this.set(attrs, options);

_.extend({}, defaults, attrs)defaults 放在第一位,但随后,它们将被传递的 attrs.

覆盖

如何在模型中使用 collection?

以下是实现此目的的三种解决方案。仅使用其中之一,或根据以下内容创建您自己的。

最简单有效的方法是不要

Todocollection 排除在 Daymodel 模型之外,只在需要时才创建 collection,就像 假设的 DayView:

var DayView = Backbone.View.extend({
    initialize: function() {
        // create the collection in the view directly
        this.agenda = new Todocollection(this.model.get('agenda'));
    },
    /* ...snip... */
});

然后,当您想要在模型中保留更改时,只需将 collection 模型放回 Daymodel:

this.model.set('agenda', this.collection.toJSON());

将collection放入模型属性中

您可以创建一个函数来延迟创建 collection 并将其作为 属性、 保留在模型中 attributes哈希清理.

var Daymodel = Backbone.Model.extend({
    defaults: { day: 1, },
    getAgenda: function() {
        if (!this.agenda) this.agenda = new Todocollection(this.get('agenda'));
        return this.agenda;
    }
});

然后,该模型控制 collection,它可以轻松地与已经共享该模型的所有对象共享,每个实例只创建一个 collection。

保存模型时,您仍然需要将原始模型传回 attributes 哈希。

一个collection里面的属性

您可以完成您已经尝试做的小改动。

  1. 切勿将 object 放入 defaults

    ...而不是使用返回 object 的函数。

    var Daymodel = Backbone.Model.extend({
        defaults: function() {
            return {
                day: 1,
                agenda: new Todocollection()
            };
        },
    });
    

    否则,agenda collection 将在 Daymodel 的每个实例之间共享,因为 collection 在创建 Daymodel [ 时仅创建一次=144=].

    这也适用于 object 字面量、数组、函数(你为什么要把它放在 defaults 中?!)。

  2. 确保它始终是 collection.

    var Daymodel = Backbone.Model.extend({
        defaults: { day: 1, },
        initialize: function(attrs, options) {
            var agenda = this.getAgenda();
            if (!(agenda instanceof Todocollection)) {
                // you probably don't want a 'change' event here, so silent it is.
                return this.set('agenda', new Todocollection(agenda), { silent: true });
            }
        },
        /**
         * Parse can overwrite attributes, so you must ensure it's a collection
         * here as well.
         */
        parse: function(response) {
            if (_.has(response, 'agenda')) {
                response.agenda = new Todocollection(response.agenda);
            }
            return response;
        },
        getAgenda: function() {
            return this.get('agenda');
        },
        setAgenda: function(models, options) {
            return this.getAgenda().set(models, options);
        },
    });
    
  3. 确保它是可序列化的。

    var Daymodel = Backbone.Model.extend({
        /* ...snip... */
        toJSON: function(options) {
            var attrs = Daymodel.__super__.toJSON.apply(this, arguments),
                agenda = attrs.agenda;
            if (agenda) {
                attrs.agenda = agenda.toJSON(options);
            }
            return attrs;
        },
    });
    

    如果您将 collection 放入模型 属性 中,这很容易应用,如上文所述。

  4. 避免意外覆盖 agenda 属性。

    这与第 2 点并存,这就是它变得越来越难的地方,因为它很容易被忽视,或者其他人(或其他库)可能会这样做。

    可以重写 saveset 函数来添加检查,但是它变得过于复杂而在长 运行.

模型中 collection 的缺点是什么?

我谈到了在模型中完全避免它,或者懒惰地创建它。那是因为如果你实例化很多模型,它会变得非常慢,如果每个模型嵌套多次(模型有 collection 个模型,有其他 collection 个模型,等等) .

按需创建时,您仅在需要时才使用机器资源,并且仅在需要时才使用。例如,任何现在不在屏幕上的模型都不会创建 collection。

开箱即用的解决方案

也许要让它正常工作需要太多工作,所以一个完整的解决方案可能会有所帮助,而且有几个。