“Як” зберегти всю колекцію в 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? Або вам потрібно зберігати кожну модель окремо? Редагувати: Ах, читайте уважніше, сервер МОЖЕ "масово оновлювати / створювати"
Едвард М Сміт,

@Edward: Так! Ясно сказав, оскільки це, як правило, викликає занепокоєння, але не в цьому випадку :)
кандидат наук

Отже, яка структура даних, які очікує отримати сервер?
Edward M Smith

@Edward: Чи має значення структура даних? Форматування в коментарі неможливе, але це приблизно так: [{postId: 1, ярлики: [{id: 1, name: "a"}, {id: 2, name: "b"}]}] В основному кожен " postId "може мати набір / масив міток, які самі є об'єктами. Таких публікацій може бути багато ... Я не думаю, що формат даних має щось спільне з даною проблемою, якщо я чогось не пропустив
PhD

Відповіді:


64

Моя безпосередня думка полягає в тому, щоб не замінити метод методу збереження на Backbone.Collection, а обернути колекцію в інший Backbone.Model і замінити метод toJSON на цьому. Тоді Backbone.js буде розглядати модель як єдиний ресурс, і вам не доведеться зламати те, як багато думає backone.

Зверніть увагу, що Backbone.Collection має метод toJSON, тому більшість ваших робіт виконується за вас. Вам просто потрібно проксі-метод toJSON вашої обгортки Backbone.Model для колекції Backbone.

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
Я переглянув вихідний код і, схоже, у колекціях немає методу збереження, тому перевизначення не повинно бути проблемою (якби у нього був метод збереження, світ був би НАБАГАТО простішим :)
кандидат наук

+1 за ідею обгортки - чиста і солодка. Не думав про це взагалі. Я думав, що, можливо, мені доведеться зателефонувати Backboard.sync безпосередньо і просто передати колекцію замість аргументу "модель". Потрібно було б вказати URL-адресу моделі, щоб вона працювала, хоча ... будь-які думки? Оскільки метод синхронізації внутрішньо просто викликає getURL(model)аргумент моделі і не виконує жодного типу порівнянь ... здається, навмисне за задумом
PhD

3
Погодьтеся - дуже елегантно. Однак у мене виникає проблема з цим рішенням: моя модель обгортки завжди нова, тому, коли я зберігаю (), це завжди POST. Я вже отримав свої дані, тому, коли я зберігаю (), це має бути 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..є більш зворотним.
yves amsellem

Я думаю - для типу - оновлення було б краще, ніж створити ... До речі. ви можете замінити toJSON моделі або колекції, щоб ви могли регулювати, що надсилати на сервер ... (зазвичай потрібен лише атрибут id) У Backbone.Relational ви також можете встановити, що додавати у формат json.
inf3rno

1
Backbone.sync очікує «створити», див backbonejs.org/docs/backbone.html#section-141
hacklikecrack

8

У підсумку я просто мав метод, схожий на "збереження", і всередині нього викликав $ .ajax. Це дало мені більше контролю над ним, без необхідності додавати клас обгортки, як запропонував @brandgonesurfing (хоча мені дуже подобається ця ідея :) Як вже згадувалося, оскільки у мене вже був метод collection.toJSON (), який перевизначив усе, що я зробив, використовуючи його у дзвінку ajax ...

Сподіваюся, це допомагає тому, хто натрапляє на це ...


3
Вам було б краще зателефонувати до Backbone.ajax (він все одно є проксі-сервером jQuery, але це робить його більш ремонтопридатним)
developerbmw

5

Це насправді залежить від того, який договір між клієнтом і сервером. Ось спрощений приклад CoffeeScript, коли PUT to /parent/:parent_id/childrenwith {"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').


Атрибут параметрів "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);
    }
});

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 callbacks:

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());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.