嵌套在firebase集合模型中的集合没有添加函数

时间:2016-11-24 05:40:00

标签: javascript backbone.js firebase backbone-collections

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

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

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...");
    },
});

然后有一个高级模型,它包含sublevel集合作为属性。

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
    });

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

但它不起作用。该错误表明this.collection.last(...).get(...).add不是函数。

调试后,我发现this.collection.last().get("agenda")返回一个通用JS对象而不是集合对象。

我进一步调试了如果我使用骨干集合作为外部集合DayCollection。一切都很顺利。

如何解决这个问题?

1 个答案:

答案 0 :(得分:3)

为什么默认集合属性不再是集合?

当您获取或创建新的Daymodel时,我认为它是这样的:

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

首先是agenda的默认Todocollection属性被原始对象数组取代。 Backbone不知道agenda是一个集合,不会自动填充它。

这就是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覆盖。

如何在模型中使用集合?

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

最简单,最有效的方法是

Todocollection保留在Daymodel模型之外,只在需要时创建该集合,例如假设 DayView

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

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

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

将集合放入模型的属性

您可以创建一个懒惰地创建集合并将其作为属性保留在模型中的函数,而不是属性,保持attributes哈希干净

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

然后,模型控制集合,它可以与已经共享模型的所有内容轻松共享,每个实例只创建一个集合。

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

属性

内的集合

您可以通过微小的更改来完成您已经尝试做的事情。

  1. 切勿将对象放入defaults

    ...而不使用返回对象的函数。

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

    否则,agenda集合将在Daymodel的每个实例之间共享,因为在创建Daymodel类时仅创建一次集合。

    这也适用于对象文字,数组,函数(为什么你还要把它放在defaults?)。

  2. 确保它始终是一个集合。

    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;
        },
    });
    

    如果您将集合放在模型属性中,如上所述,这很容易应用。

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

    这与第2点并列,因为它很容易被忽视,或者其他人(或其他lib)可以做到这一点。

    可以覆盖saveset函数来添加检查,但从长远来看,它会变得过于复杂而没有太大的好处。

  5. 模特中收藏的缺点是什么?

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

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

    开箱即用的解决方案

    也许让这项工作正常工作太多了,所以一个完整的解决方案可能有所帮助,而且还有一对。