Сэкономить сразу несколько моделей магистральных сетей

У меня есть коллекция Backbone с загрузкой моделей.

Всякий раз, когда на модели задан определенный атрибут, и он сохраняется, выполняется загрузка вычислений и пользовательских интерфейсов.

Но я хочу иметь возможность устанавливать атрибуты сразу на нескольких моделях и выполнять только сохранение и повторное воспроизведение, как только они будут установлены. Конечно, я не хочу делать несколько HTTP-запросов для одной операции и, разумеется, не хочу переписывать интерфейс десять раз.

Я надеялся найти метод сохранения на Backbone.Collection, который будет определять, какие модели hasChanged(), ударить их вместе как json и отправить на задний план. Затем рендеринг может быть вызван событием в коллекции. Нет такой удачи.

Это похоже на довольно распространенное требование, поэтому интересно, почему Backbone не реализует. Это противоречит архитектуре RESTful, чтобы сохранить несколько вещей в одной конечной точке? Если да, то что? Практически невозможно сделать 1000 запросов на сохранение 1000 мелких предметов.

Итак, это единственное решение для увеличения Backbone.Collection с моим собственным методом сохранения, который выполняет итерацию по всем его моделям и создает json для всех тех, которые изменились, и отправляет их обратно на задний план? или у кого-нибудь есть более аккуратное решение (или я просто что-то пропустил!)?

Ответ 1

Я закончил добавление Backbone.Collection с помощью нескольких методов для этого.

SaveChangeMethod создает фиктивную модель для передачи на Backbone.sync. Все основные методы синхронизации требуют от модели ее свойства url и метода JSON, поэтому мы можем легко сбить это.

Внутри модели метод 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;
}

Несмотря на то, что это незначительное злоупотребление предполагаемой использованием парадигмы измененной модели магистральных сетей, все дело в том, что мы не хотим, чтобы что-либо происходило (т.е. любые события, которые нужно было отключить) при изменении модели.

Поэтому мы должны передать {silent: true} метод model(), поэтому имеет смысл использовать магистраль hasChanged() для флага моделей, ожидающих сохранения. Конечно, это было бы проблематично, если бы вы молчали изменения моделей для других целей - collection.saveChanged() также сохранил бы их, поэтому стоит подумать об установке альтернативного флага.

В любом случае, если мы делаем это, при сохранении, нам нужно убедиться, что в базе теперь думают, что модели не изменились (без запуска их изменений), поэтому нам нужно вручную манипулировать моделью, как будто это не было изменено. Метод saveChanged() выполняет итерацию по нашим измененным моделям и вызывает этот метод changeSilently() для модели, который в основном представляет собой метод Backbone model.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 должен заменить всю коллекцию, хотя до тех пор, пока мое приложение никогда не понадобится заменить всю коллекцию, я с удовольствием принимаю прагматичный подход.

Ответ 2

Вы можете определить новый ресурс для выполнения такого поведения, вы можете назвать его MyModelBatch.

Вам необходимо реализовать новый ресурс на вашей стороне сервера, который способен переваривать модели Array и выполнить правильное действие: CREATE, UPDATE и DESTROY.

Также вам нужно реализовать Model на стороне клиента Backbone с одним атрибутом, который является массивом моделей, и специальным url, который не использует id.

О переориентации я предлагаю вам попробовать иметь один вид по каждой модели, поэтому будет так же, как и модели, но они будут детализированы повторно без дублирования.