Моя команда работает с интересной схемой данных. По сути, у нас есть документ с именем и версией, а также с набором принадлежащих ему элементов. Сами элементы не имеют связанных с ними идентификаторов, потому что они принадлежат к более крупной модели, но элементы могут быть добавлены / отредактированы / удалены из основного документа. Модель выглядит примерно так:
{
название: "Тест",
версия: 1,
Предметы : [
{имя: "Товар 1",
позиция: 0},
{name: "Пункт 2",
позиция: 1}]
}
Было бы здорово использовать коллекцию для базовых элементов в модели, но всякий раз, когда коллекция обновляется, модель должна отправлять обратно на сервер со своим URL-адресом. Как лучше всего смоделировать это в Backbone? Я буду рад опубликовать больше в сущности, если потребуется дополнительная информация.
Я бы сказал, что у вас есть два основных варианта ... Первый - оставить предметы как ванильный атрибут. 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 спасибо, это отличный пример, очень признателен
Самый полезный комментарий
@eranation : нет, должно быть примерно так же. При использовании этого шаблона я предпочитаю не включать
items
в хэш моих атрибутов, чтобы мне не приходилось их синхронизировать. Это требует, чтобы вы вытащили объектitems
из ответа для синтаксического анализа и снова добавили его для сериализации (см. Ниже). Помимо этого, вы можете захотеть поместить логику, которую @rsim вставилinitialize
в ваш методconstructor
, и использоватьon
вместоbind
(который наполовину устарел).Я считаю, что гораздо проще, чтобы атрибуты были только мелким хешем (без вложенных коллекций, моделей и т. Д.), Где это возможно.
Кажется, это хорошо работает даже для глубоко вложенных схем. (см. документацию )