«Как» сохранить всю коллекцию в Backbone.js - Backbone.sync или jQuery.ajax?

81

Я хорошо знаю, что это можно сделать, и я просмотрел довольно много мест (в том числе: Лучшая практика для сохранения всей коллекции? ). Но мне все еще не ясно, «как именно» это написано в коде? (сообщение объясняет это на английском языке. Было бы здорово получить конкретное объяснение javascript :)

Скажем, у меня есть коллекция моделей - сами модели могут иметь вложенные коллекции. Я переопределил метод toJSON () родительской коллекции и получаю действительный объект JSON. Я хочу «сохранить» всю коллекцию (соответствующий JSON), но магистраль, похоже, не имеет встроенной функции.

var MyCollection = Backbone.Collection.extend({
model:MyModel,

//something to save?
save: function() {
   //what to write here?
 }

});

Я знаю, что где-то вы должны сказать:

Backbone.sync = function(method, model, options){
/*
 * What goes in here?? If at all anything needs to be done?
 * Where to declare this in the program? And how is it called?
 */
}

После того, как «представление» завершено с обработкой, оно отвечает за указание коллекции «сохранить» себя на сервере (способном обрабатывать массовый запрос на обновление / создание).

Возникающие вопросы:

  1. Как / что написать в коде, чтобы «связать все вместе»?
  2. Какое «правильное» расположение обратных вызовов и как указать обратный вызов «успех / ошибка»? Я имею в виду синтаксически? Мне непонятен синтаксис регистрации обратных вызовов в магистрали ...

Если это действительно сложная задача, можем ли мы вызвать jQuery.ajax в представлении и передать this.successMethodили this.errorMethodкак обратные вызовы успеха / ошибки ?? Это будет работать?

Мне нужно синхронизироваться с мышлением магистрали - я знаю, что определенно что-то упустил, синхронизируя целые коллекции.

кандидат наук
источник
Может ли ваш серверный код принять это как единый запрос? Другими словами, вся коллекция верхнего уровня, все модели и вложенные коллекции как один пакет JSON? Или вам нужно сохранять каждую модель отдельно? Изменить: Ах, читайте внимательнее, сервер способен на «массовое обновление / создание»
Эдвард М. Смит
@ Эдвард: Ага! Сделал это явным, поскольку обычно это вызывает беспокойство, но не в данном случае :)
PhD
Итак, какова структура данных, которые сервер ожидает получить?
Эдвард М. Смит
@ Эдвард: Имеет ли значение структура данных? В комментарии невозможно форматирование, но оно выглядит следующим образом: [{postId: 1, labels: [{id: 1, name: "a"}, {id: 2, name: "b"}]}] В основном каждый " postId "может иметь набор / массив меток, которые сами по себе являются объектами. Таких сообщений может быть много ... Я не думаю, что формат данных имеет какое-либо отношение к рассматриваемой проблеме, если только я чего-то не
упускаю
См. Также stackoverflow.com/questions/7975316/…
philfreo

Ответы:

64

Моя немедленная мысль - не переопределять метод метода сохранения в Backbone.Collection, а обернуть коллекцию в другой Backbone.Model и переопределить метод toJSON для этого. Тогда Backbone.js будет рассматривать модель как единый ресурс, и вам не придется слишком много ломать голову, как backone думает.

Обратите внимание, что Backbone.Collection имеет метод toJSON, поэтому большая часть вашей работы выполняется за вас. Вам просто нужно проксировать метод toJSON вашей оболочки Backbone.Model на Backbone.collection.

var MyCollectionWrapper = Backbone.Model.extend({
url: "/bulkupload",

//something to save?
toJSON: function() {
    return this.model.toJSON(); // where model is the collection class YOU defined above
 }

});
брэдгонсерфинг
источник
3
Я просмотрел исходный код, и мне кажется, что у коллекций нет метода сохранения, поэтому переопределение не должно быть проблемой (если бы у него был метод сохранения, мир был бы НАМНОГО проще :)
PhD
+1 за идею обертки - чисто и мило. Совсем не думал об этом. Я подумал, что, возможно, мне придется вызвать Backboard.sync напрямую и просто передать коллекцию вместо аргумента «модель». Нужно будет указать URL-адрес модели, чтобы она работала ... есть мысли? Поскольку метод синхронизации внутри просто вызывает getURL(model)аргумент модели и не выполняет никаких сравнений ... кажется, намеренно намеренно
PhD
3
Согласитесь - очень элегантно. Однако у меня возникла проблема с этим решением: моя модель оболочки всегда новая, поэтому, когда я save (), это всегда POST. Я уже получил свои данные, поэтому, когда я save (), это должно быть PUT. Полагаю, я могу жестко закодировать isNew () = false или установить поддельный идентификатор, но это не кажется элегантным решением. У вас есть какие-нибудь предложения?
Скотт Свитцер
1
Действительно чистый ответ, было бы неплохо увидеть, как вы создадите экземпляр CollectionWrapper.
Энтони
Да, это сработало и у меня, и +1 за хорошие усилия, но если я хочу отправить две коллекции на сервер, как мне это сделать?
CodeNotFound
25

Очень простой ...

Backbone.Collection.prototype.save = function (options) {
    Backbone.sync("create", this, options);
};

... предоставит вашим коллекциям метод сохранения. Имейте в виду, что это всегда будет отправлять все модели коллекции на сервер независимо от того, что изменилось. Параметры - это обычные параметры jQuery ajax.

взломать
источник
4
Кажется правильным. Может быть добавление return Backbone.sync..еще больше Backbonish.
Ив Амселлем
Думаю - по типу - обновление было бы лучше, чем создавать ... Кстати. вы можете переопределить toJSON модели или коллекции, чтобы вы могли регулировать, что отправлять на сервер ... (обычно нужен только атрибут id). В Backbone.Relational вы также можете указать, что добавлять в формат json.
inf3rno
1
Backbone.sync ожидает «создать», см backbonejs.org/docs/backbone.html#section-141
hacklikecrack
8

В итоге у меня просто был метод, похожий на «save», и я вызвал в нем $ .ajax. Это дало мне больше контроля над ним без необходимости добавлять класс-оболочку, как предлагал @brandgonesurfing (хотя мне очень нравится эта идея :) Как уже упоминалось, поскольку у меня уже был метод collection.toJSON (), который переопределил все, что я делал, это использовал в вызове ajax ...

Надеюсь, это поможет тому, кто наткнется на это ...

кандидат наук
источник
3
Вам лучше позвонить Backbone.ajax (он в любом случае проксирует jQuery, но делает его более
developerbmw
5

Это действительно зависит от того, какой контракт заключен между клиентом и сервером. Вот упрощенный пример CoffeeScript, в котором PUT to /parent/:parent_id/childrenс {"children":[{child1},{child2}]}заменяет дочерние элементы родителя тем, что находится в PUT, и возвращает {"children":[{child1},{child2}]}:

class ChildElementCollection extends Backbone.Collection
  model: Backbone.Model
  initialize: ->
    @bind 'add', (model) -> model.set('parent_id', @parent.id)

  url: -> "#{@parent.url()}/children" # let's say that @parent.url() == '/parent/1'
  save: ->
    response = Backbone.sync('update', @, url: @url(), contentType: 'application/json', data: JSON.stringify(children: @toJSON()))
    response.done (models) => @reset models.children
    return response

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

Если ваш сервер в порядке с PUT [{child1},{child2], тогда ваша строка Backbone.sync может измениться на response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json').

Carpeliam
источник
Атрибут опций «url» здесь не обязателен =)
Сергей Камардин
5

Ответ зависит от того, что вы хотите делать с коллекцией на стороне сервера.

Если вам нужно отправить дополнительные данные с сообщением, вам может потребоваться модель оболочки или реляционная модель. .

С моделью оболочки вам всегда нужно писать свой собственный метод синтаксического анализа :

var Occupants = Backbone.Collection.extend({
    model: Person
});

var House = Backbone.Model.extend({
    url: function (){
        return "/house/"+this.id;
    },
    parse: function(response){
        response.occupants = new Occupants(response.occupants)
        return response;
    }
});

Я думаю, что реляционные модели лучше, потому что вы можете их проще настраивать, и вы можете регулировать с помощьюопции includeInJSON, какие атрибуты следует помещать в json, который вы отправляете в службу отдыха.

var House = Backbone.RelationalModel.extend({
    url: function (){
        return "/house/"+this.id;
    },
    relations: [
        {
            type: Backbone.HasMany,
            key: 'occupants',
            relatedModel: Person,
            includeInJSON: ["id"],
            reverseRelation: {
                key: 'livesIn'
            }
        }
    ]
});

Если вы не отправляете дополнительные данные , вы можете синхронизировать саму коллекцию . В этом случае вам нужно добавить метод сохранения в свою коллекцию (или прототип коллекции):

var Occupants = Backbone.Collection.extend({
    url: "/concrete-house/occupants",
    model: Person,
    save: function (options) {
        this.sync("update", this, options);
    }
});
inf3rno
источник
3

Я также был удивлен, что коллекции Backbone не имеют встроенного сохранения. Вот что я положил в свою базовую коллекцию для этого. Я определенно не хочу перебирать каждую модель в коллекции и сохранять независимо. Кроме того, я использую Backbone на бэкэнде с помощью Node, поэтому я переопределяю собственный Backbone.syncкод для сохранения в плоский файл в моем небольшом проекте, но код должен быть почти таким же:

    save: function(){                                                                                                                                                                                                                                                                                                                                                     
      Backbone.sync('save', this, {                                                                                                                                                                                                                                                                                                                                     
        success: function(){                                                                                                                                                                                                                                                                                                                                          
          console.log('users saved!');                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                             
      });                                                                                                                                                                                                                                                                                                                                                               
    }
Мовис Ледфорд
источник
Также имеет смысл просто передать параметры, например,save: function (options) { Backbone.sync('save', this, options); }
Мэтт Флетчер
3

Старый поток, который я знаю, в итоге я сделал следующее:

Backbone.Collection.prototype.save = function (options) {
            // create a tmp collection, with the changed models, and the url
            var tmpCollection = new Backbone.Collection( this.changed() );
            tmpCollection.url = this.url;
            // sync
            Backbone.sync("create", tmpCollection, options);
        };
        Backbone.Collection.prototype.changed = function (options) {
            // return only the changed models.
            return this.models.filter( function(m){
                return m.hasChanged()
            });
        };
// and sync the diffs.
self.userCollection.save();

Довольно натянуто вперед :)

Рене Ветелинг
источник
2

Вот простой пример:

var Books = Backbone.Collection.extend({
model: Book,
url: function() {
  return '/books/';
},
save: function(){
  Backbone.sync('create', this, {
    success: function() {
      console.log('Saved!');
    }
  });
 }
});

Когда вы вызываете метод save () в своей коллекции, он отправляет запрос метода PUT на определенный URL-адрес.

Гаурав Гупта
источник
Мне было интересно, можете ли вы помочь решить мою проблему с сохранением коллекции, прямо сейчас jsfiddle.net/kyllle/f1h4cz7f/3 toJSON () обновляет каждую модель, но тогда в сохранении, похоже, нет данных?
Стайлер
1

Я бы попробовал что-то вроде:

var CollectionSync = function(method, model, [options]) {
    // do similar things to Backbone.sync
}

var MyCollection = Backbone.Collection.extend({
    sync: CollectionSync,
    model: MyModel,
    getChanged: function() {
        // return a list of models that have changed by checking hasChanged()
    },
    save: function(attributes, options) {
        // do similar things as Model.save
    }
});

( https://stackoverflow.com/a/11085198/137067 )

Филфрео
источник
1

Принятый ответ довольно хорош, но я могу пойти еще дальше и дать вам код, который обеспечит запуск правильных событий для ваших слушателей, а также позволит вам передать опцию обратного вызова событий ajax:

save: function( options ) {
  var self = this;

  var success = options.success;
  var error = options.error;
  var complete = options.complete;

  options.success = function( response, status, xhr ) {
    self.trigger('sync', self, response, options);
    if (success) return success.apply(this, arguments);
  };

  options.error = function( response, status, xhr ) {
    self.trigger('error', self, response, options);
    if (error) return error.apply(this, arguments);
  };

  options.complete = function( response, status, xhr ) {
    if (complete) return complete.apply(this, arguments);
  }

  Backbone.sync('create', this, options);
}
Дроссельная заслонка
источник
0

Для тех, кто все еще использует backbone.js в 2017 году, принятый ответ не работает.

Попробуйте удалить переопределение toJSON () в модели оболочки и вызвать toJSON в коллекции при создании экземпляра оболочки модели.

new ModelWrapper(Collection.toJSON());
нули и единицы
источник