Backbone: ¿Cuál es la mejor manera de modelar una colección dentro de un modelo?

Creado en 4 nov. 2010  ·  11Comentarios  ·  Fuente: jashkenas/backbone

Mi equipo está trabajando con un esquema de datos interesante. Básicamente, lo que tenemos es un documento que tiene un nombre y una versión y una serie de elementos que le pertenecen. Los elementos en sí no tienen identificadores asociados porque pertenecen al modelo más grande, pero los elementos se pueden agregar / editar / eliminar del documento principal. El modelo se parece a esto:
{
nombre: "Prueba",
versión 1,
elementos : [
{nombre: "Elemento 1",
posición: 0},
{nombre: "Elemento 2",
posición: 1}]
}

Sería genial usar una colección para los elementos subyacentes en el modelo, pero cada vez que la colección se actualiza, el modelo debe volver a publicarse en el servidor con su URL. ¿Cuál es la mejor manera de modelar esto en Backbone? Estaré feliz de publicar más en resumen si se necesita más información.

question

Comentario más útil

@eranation : no, debería ser más o menos lo mismo. Cuando utilizo este patrón, me gusta mantener el items fuera de mi hash de atributos para no tener que mantenerlos sincronizados. Esto requiere que extraiga el objeto items de la respuesta para analizarlo y vuelva a agregarlo para serializarlo (ver más abajo). Aparte de eso, es posible que desee poner la lógica que @rsim puso en el initialize en su método constructor lugar, y usar on lugar de bind (que está semi-en desuso).

Encuentro que es mucho más fácil que los atributos sean solo un hash superficial (sin colecciones anidadas, modelos, etc.), siempre que sea posible.

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}
  ]
});

Esto parece funcionar bien incluso para esquemas profundamente anidados. (ver los documentos )

Todos 11 comentarios

Yo diría que tiene dos opciones principales ... La primera es dejar los elementos como un atributo de vainilla. Backbone utiliza una verificación de igualdad profunda cuando cambian los atributos, por lo que si se actualiza un elemento interno, el documento lo sabrá.

La otra opción es sacar los elementos del documento y adjuntarlos como modelos por derecho propio, dentro de una colección pegada en el documento (hacemos algo en este sentido en DocumentCloud). Por ejemplo (en términos generales):

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

¿Alguno de esos funciona bien para usted?

También recomendaría agregar la propiedad "documento" al objeto Documento al que se puede acceder cuando sea necesario desde los elementos:

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}
  ]
});

Creo que vincular el evento de cambio de los elementos al guardado del documento es la pieza que falta. Sin embargo, la adición del documento al ItemSet también parece ser muy útil. Intentaremos esto hoy y les haré saber cómo resultó.

Esto funcionó bien. El mayor problema que tenemos ahora es con la mongoide. Gracias por la ayuda chicos.

¿Cómo haría para guardar todos los elementos a la vez? No quiero hacer una solicitud cada vez que se cambia un elemento, pero quiero guardarlos todos con una sola solicitud sobre la acción del usuario. Estaba pensando en recopilar todo cuando se guarda el documento y actualizar el atributo 'elementos' del documento. ¿Es esa una buena solución?

Siempre que sus elementos estén configurados en el documento, cuando haga un document.save (), los elementos también se enviarán al servidor.

Pero digamos que si agrego un elemento a la colección document1.items en tiempo de ejecución, no se agrega al atributo 'items' de document1 también automágicamente. Entonces, si hago un document1.save (), el nuevo modelo que agregué a la colección no se enviará al servidor. No veo cómo los cambios en la colección podrían propagarse en los atributos del modelo que se envían con guardar.

Entonces, así es como lo manejamos: el documento tiene una matriz de elementos predeterminada. En la inicialización, en un método de conjunto sobrecargado, creo una nueva colección de elementos a partir de los atributos y la configuro en el documento.

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

En ese punto, simplemente se ocupa de los métodos de recopilación de elementos con 'get', 'set', 'add' y 'remove'. No te metas con la notación de puntos. Incluso tengo métodos en mi clase de documento llamados addItem y deleteItem para activar eventos de cambio en el documento en sí. Cuando guarde () en el documento, llamará a toJSON en su colección de artículos.

Honestamente, este es solo un caso simple para nuestros documentos y tenemos subdocumentos aún más profundos. Lidiar con esta cantidad de complejidad con la columna vertebral y sobrecargar varios de los métodos en los modelos es un verdadero dolor de cabeza. Ahora estamos considerando reemplazar la columna vertebral con sproutcore en el futuro.

Si tiene que lidiar con documentos realmente complejos, le sugiero que consulte ExtJS o sproutcore. Backbone es ideal para un proyecto pequeño con modelos simples, pero se desmorona bastante rápido cuando los objetos / interacciones comienzan a aumentar.

¿Existen nuevas "mejores prácticas" para esto para 1.0?

@eranation : no, debería ser más o menos lo mismo. Cuando utilizo este patrón, me gusta mantener el items fuera de mi hash de atributos para no tener que mantenerlos sincronizados. Esto requiere que extraiga el objeto items de la respuesta para analizarlo y vuelva a agregarlo para serializarlo (ver más abajo). Aparte de eso, es posible que desee poner la lógica que @rsim puso en el initialize en su método constructor lugar, y usar on lugar de bind (que está semi-en desuso).

Encuentro que es mucho más fácil que los atributos sean solo un hash superficial (sin colecciones anidadas, modelos, etc.), siempre que sea posible.

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}
  ]
});

Esto parece funcionar bien incluso para esquemas profundamente anidados. (ver los documentos )

@ akre54 gracias, este es un gran ejemplo, muy apreciado

¿Fue útil esta página
0 / 5 - 0 calificaciones