一次保存多个 Backbone.js 模型

iecba09b  于 2022-11-10  发布在  其他
关注(0)|答案(3)|浏览(195)

我有一个 Backbone 收集与负载的模型。
每当在模型上设置特定属性并保存该属性时,就会启动计算负载并重新呈现UI。
但是,我希望能够一次在几个模型上设置属性,并且只在它们都设置好后才进行保存和重新渲染。当然,我不希望为一个操作发出几个http请求,也绝对不希望必须重新渲染界面十次。
我希望能在Backbone.Collection上找到一个保存方法,它可以计算出哪些模型hasChanged(),将它们作为json一起处理,然后发送到后端。然后,重新呈现可以由集合上的一个事件触发。没有这样的运气。
这似乎是一个很常见的要求,所以我想知道为什么Backbone没有实现。这是否违背了RESTful架构,将几个东西保存到一个端点?如果是这样,那又怎样?发出1000个请求来持久化1000个小项目是不现实的。
那么,是不是唯一一个用我自己的保存方法来扩充Backbone.Collection的解决方案,该方法迭代它的所有模型,并为所有已经更改的模型构建json,然后将其发送到后端?或者有人有更简洁的解决方案吗(或者我只是遗漏了什么!)?

33qvvth1

33qvvth11#

最后,我用几个方法扩充了Backbone.Collection来处理这个问题。
saveChangeMethod创建了一个要传递给Backbone. sync的虚拟模型。所有 Backbone 网的sync方法都需要来自模型的url属性和toJSON方法,因此我们可以轻松地实现这一点。
在内部,模型的toJSON方法只返回其属性的副本(发送到服务器),因此我们可以使用toJSON方法,只返回模型数组。Backbone.sync将其字符串化,只提供属性数据。
成功时,saveChanged会在要处理的集合上触发一次事件。我已经插入了一些代码,让它为在任何批处理模型中更改的每个属性触发一次特定事件。

Backbone.Collection.prototype.saveChanged = function () {
    var me = this,
        changed = me.getChanged(),
        dummy = {
            url: this.url,
            toJSON: function () {
                return changed.models;
            }
        },
        options = {
            success: function (model, resp, xhr) {
                for (var i = 0; i < changed.models.length; i++) {
                    changed.models[i].chnageSilently();
                }
                for (var attr in changed.attributes) {
                    me.trigger("batchchange:" + attr);
                }
                me.trigger("batchsync", changed);
            }
        };
    return Backbone.sync("update", dummy, options);
}

然后,我们只需要对集合使用getChanged()方法,它返回一个具有2个属性的对象,一个包含已更改模型的数组,以及一个标记哪些属性已更改的对象:

Backbone.Collection.prototype.getChanged = function () {
    var models = [],
        changedAttributes = {};
    for (var i = 0; i < this.models.length; i++) {
        if (this.models[i].hasChanged()) {
            _.extend(changedAttributes, this.models[i].changedAttributes());
            models.push(this.models[i]);
        }
    }
    return models.length ? {models: models, attributes: changedAttributes} : null;
}

虽然这是对 Backbone.js “更改模型”范例的轻微滥用,但批处理的全部意义在于,当模型更改时,我们不希望发生任何事情(即,任何事件触发)。
我们因此不得不通过{沉默:true}添加到模型的set()方法中,所以使用backbone的hasChanged()来标记等待保存的模型是有意义的。当然,如果您出于其他目的而静默地更改模型,这将是有问题的- collection.saveChanged()也会保存这些模型,因此值得考虑设置一个替代标记。
在任何情况下,如果我们这样做,在保存时,我们需要确保 Backbone.js 现在认为模型没有改变(而不触发它们的change事件),所以我们需要手动操作模型,就好像它没有被更改一样。()方法在我们更改的模型上迭代,并无声地调用此change()方法,这基本上就是Backbone的模型。change()方法没有触发器:

Backbone.Model.prototype.changeSilently = function () {
    var options = {},
    changing = this._changing;
    this._changing = true;
    for (var attr in this._silent) this._pending[attr] = true;
    this._silent = {};
    if (changing) return this;

    while (!_.isEmpty(this._pending)) {
        this._pending = {};
        for (var attr in this.changed) {
        if (this._pending[attr] || this._silent[attr]) continue;
        delete this.changed[attr];
        }
        this._previousAttributes = _.clone(this.attributes);
    }
    this._changing = false;
    return this;
}

用法:

model1.set({key: value}, {silent: true});
model2.set({key: value}, {silent: true});
model3.set({key: value}, {silent: true});
collection.saveChanged();

RE. RESTfulness..对集合的端点执行PUT来更改其中的“一些”记录是不太正确的。从技术上讲,PUT应该替换整个集合,尽管在我的应用程序真正需要替换整个集合之前,我很乐意采取实用的方法。

0tdrvxhp

0tdrvxhp2#

您可以定义一个新的 resource 来完成这种行为,您可以将其称为MyModelBatch
您需要在服务器端实现一个新的资源,它能够处理Array的模型并执行适当的操作:CREATEUPDATEDESTROY中的一个或多个。
此外,您还需要在您的 Backbone 客户端中实现一个Model,该Model具有一个 * 属性 *(即模型数组)和一个特殊的url,该url不使用id
关于 * 重新渲染 * 的事情,我建议你尝试每个模型都有一个视图,这样当模型发生变化时,就会有同样多的 * 渲染 *,但它们将是没有重复的细节 * 重新渲染 *。

qjp7pelc

qjp7pelc3#

这是我想出来的

Backbone.Collection.extend({
    saveAll: function(models, key, val, options) {

        var attrs, xhr, wait, that = this;

        var transport = {
            url: this.url,
            models: [],
            toJSON: function () {
                return { models: this.models };
            },
            trigger: function(){
                return that.trigger.apply(that, arguments);
            }
        };

        if(models == null){
            models = this.models;
        }

        // Handle both `"key", value` and `{key: value}` -style arguments.
        if (key == null || typeof key === 'object') {
            attrs = key;
            options = val;
        } else {
            (attrs = {})[key] = val;
        }

        options = _.extend({validate: true}, options);
        wait = options.wait;

        // After a successful server-side save, the client is (optionally)
        // updated with the server-side state.
        if (options.parse === void 0) options.parse = true;

        var triggers = [];

        _.each(models, function(model){

            var attributes = model.attributes;

            // If we're not waiting and attributes exist, save acts as
            // `set(attr).save(null, opts)` with validation. Otherwise, check if
            // the model will be valid when the attributes, if any, are set.
            if (attrs && !wait) {
                if (!model.set(attrs, options)) return false;
            } else {
                if (!model._validate(attrs, options)) return false;
            }

            // Set temporary attributes if `{wait: true}`.
            if (attrs && wait) {
                model.attributes = _.extend({}, attributes, attrs);
            }

            transport.models.push(model.toJSON());

            triggers.push(function(resp){
                if(resp.errors){
                    model.trigger('error', model, resp, options);
                } else {
                    // Ensure attributes are restored during synchronous saves.
                    model.attributes = attributes;
                    var serverAttrs = options.parse ? model.parse(resp, options) : resp;
                    if (wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
                    if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
                        return false;
                    }
                    model.trigger('sync', model, resp, options);
                }
            });

            // Restore attributes.
            if (attrs && wait) model.attributes = attributes;
        });

        var success = options.success;
        options.success = function(resp) {
            _.each(triggers, function(trigger, i){
                trigger.call(options.context, resp[i]);
            });
            if (success) success.call(options.context, models, resp, options);
        };
        return this.sync('create', transport, options);
    }
});

相关问题