Backbone: Как лучше всего смоделировать коллекцию внутри модели?

Созданный на 4 нояб. 2010  ·  11Комментарии  ·  Источник: jashkenas/backbone

Моя команда работает с интересной схемой данных. По сути, у нас есть документ с именем и версией, а также с набором принадлежащих ему элементов. Сами элементы не имеют связанных с ними идентификаторов, потому что они принадлежат к более крупной модели, но элементы могут быть добавлены / отредактированы / удалены из основного документа. Модель выглядит примерно так:
{
название: "Тест",
версия: 1,
Предметы : [
{имя: "Товар 1",
позиция: 0},
{name: "Пункт 2",
позиция: 1}]
}

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

question

Самый полезный комментарий

@eranation : нет, должно быть примерно так же. При использовании этого шаблона я предпочитаю не включать items в хэш моих атрибутов, чтобы мне не приходилось их синхронизировать. Это требует, чтобы вы вытащили объект items из ответа для синтаксического анализа и снова добавили его для сериализации (см. Ниже). Помимо этого, вы можете захотеть поместить логику, которую @rsim вставил initialize в ваш метод constructor , и использовать on вместо bind (который наполовину устарел).

Я считаю, что гораздо проще, чтобы атрибуты были только мелким хешем (без вложенных коллекций, моделей и т. Д.), Где это возможно.

var Document = Backbone.Model.extend({
  constructor: function() {
    this.items = new ItemSet(null, {document: this});
    this.items.on('change', this.save, this);
    Backbone.Model.apply(this, arguments);
  },
  parse: function(resp) {
    this.items.set(resp.items, {parse: true, remove: false});
    delete resp.items;
    return resp;
  },
  toJSON: function() {
    var attrs = _.clone(this.attributes);
    attrs.items = this.items.toJSON();
    return attrs;
  }
});
var ItemSet = Backbone.Collection.extend({
  model: Item,
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Кажется, это хорошо работает даже для глубоко вложенных схем. (см. документацию )

Все 11 Комментарий

Я бы сказал, что у вас есть два основных варианта ... Первый - оставить предметы как ванильный атрибут. Backbone использует глубокую проверку равенства при изменении атрибутов, поэтому, если внутренний элемент обновлен, документ узнает об этом.

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

var Document = Backbone.Model.extend({
  initialize: function() {
    this.items = new ItemSet();
    this.items.bind('change', this.save);
  }
});

Вам подходит что-то из этого?

Я бы также порекомендовал добавить свойство «документ» обратно в объект Document, к которому при необходимости можно было бы получить доступ из элементов:

var Document = Backbone.Model.extend({
  initialize: function() {
    this.items = new ItemSet(this.get('items'), {document: this});
    this.items.bind('change', this.save);
  }
});
var ItemSet = Backbone.Collection.extend({
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Я считаю, что привязка события изменения элементов к сохранению документа является недостающей частью. Хотя добавление документа в ItemSet также выглядит очень полезным. Мы попробуем это сегодня, и я дам вам знать, что из этого получится.

Это хорошо сработало. Самая большая проблема, с которой мы сейчас сталкиваемся, связана с монгоидом. Спасибо за помощь, ребята!

Как мне сохранить сразу все предметы? Я не хочу делать запрос каждый раз, когда элемент изменяется, но я хочу сохранить их все с помощью одного запроса на действие пользователя. Я думал о том, чтобы собрать все, когда документ сохранен, и обновить атрибут «items» документа. Это хорошее решение?

Пока ваши элементы установлены в документе, тогда, когда вы выполните document.save (), элементы также будут отправлены на сервер.

Но предположим, что если я добавлю элемент в коллекцию document1.items во время выполнения, он не будет добавлен в атрибут «items» документа document1 также автоматически. Поэтому, если я затем сделаю document1.save (), новая модель, которую я добавил в коллекцию, не будет отправлена ​​на сервер. Я не вижу, как изменения в коллекции могут распространяться на атрибуты модели, отправленные с сохранением.

Итак, вот как мы с этим справляемся: в документе есть массив элементов по умолчанию. При инициализации в методе перегруженного набора я создаю новую коллекцию элементов из атрибутов и устанавливаю ее в документе.

class Document extends Backbone.Model
  defaults:
    items: []

  set: (attrs, options) ->
    items = attrs['items']
    if _( items ).isArray()
      if _( items ).isEmpty()
        attrs['items'] = new DocumentItemsCollection
        newItem = new Item
        attrs['items'].add(newItem, { silent: true })
      else
        attrs['items'] = new DocumentItemsCollection items

На этом этапе вы просто имеете дело с методами сбора элементов с помощью get, set, add и remove. Не связывайтесь с точечной нотацией. У меня даже есть методы в моем классе Document, называемые addItem и deleteItem, для запуска событий изменения в самом документе. Когда вы выполняете save () в документе, он вызывает JSON в вашей коллекции элементов.

Честно говоря, это простой случай для наших документов, а у нас есть еще более глубокие суб-документы. Справиться с такой сложностью, связанной с позвоночником, и перегрузить несколько методов на моделях - это настоящая боль в заднице. Сейчас мы рассматриваем возможность замены backbone на sproutcore в будущем.

Если вам приходится иметь дело с действительно сложными документами, я бы посоветовал взглянуть на ExtJS или sproutcore. Backbone отлично подходит для небольшого проекта с простыми моделями, но довольно быстро разваливается, когда объекты / взаимодействия начинают расти.

Есть ли какие-нибудь новые «лучшие практики» для этого для 1.0?

@eranation : нет, должно быть примерно так же. При использовании этого шаблона я предпочитаю не включать items в хэш моих атрибутов, чтобы мне не приходилось их синхронизировать. Это требует, чтобы вы вытащили объект items из ответа для синтаксического анализа и снова добавили его для сериализации (см. Ниже). Помимо этого, вы можете захотеть поместить логику, которую @rsim вставил initialize в ваш метод constructor , и использовать on вместо bind (который наполовину устарел).

Я считаю, что гораздо проще, чтобы атрибуты были только мелким хешем (без вложенных коллекций, моделей и т. Д.), Где это возможно.

var Document = Backbone.Model.extend({
  constructor: function() {
    this.items = new ItemSet(null, {document: this});
    this.items.on('change', this.save, this);
    Backbone.Model.apply(this, arguments);
  },
  parse: function(resp) {
    this.items.set(resp.items, {parse: true, remove: false});
    delete resp.items;
    return resp;
  },
  toJSON: function() {
    var attrs = _.clone(this.attributes);
    attrs.items = this.items.toJSON();
    return attrs;
  }
});
var ItemSet = Backbone.Collection.extend({
  model: Item,
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Кажется, это хорошо работает даже для глубоко вложенных схем. (см. документацию )

@ akre54 спасибо, это отличный пример, очень признателен

Была ли эта страница полезной?
0 / 5 - 0 рейтинги