2012-09-10 16 views
6

Creo que tengo un problema bastante simple que es bastante difícil de encontrar y por lo tanto es difícil encontrar una solución. Configuración:Fundir/inicializar submodelos de un Backbone Modelo

  • PathCollection es un Backbone.Collection de Caminos
  • camino es un Backbone.Model que contiene NodeCollection (que es un Backbone.Collection) y EdgeCollection (que es un Backbone.Collection).

Cuando voy a buscar PathCollection

paths = new PathCollection() 
paths.fetch() 

obviamente, Caminos conseguir instancia. Sin embargo, me falta el lugar donde puedo permitir que una ruta cree una instancia de sus submodelos a partir de los valores hash de atributo. Realmente no puedo usar el análisis sintáctico, ¿verdad? Básicamente estoy buscando el punto de entrada para un modelo cuando se instancia y se establece con atributos. Siento que debe haber alguna convención para eso.

+1

¿Ha intentado utilizar el método [initialize] (http://documentcloud.github.com/backbone/#Model-constructor)? – Jack

+0

funcionaría cuando me inicialice con la Colección, pero ¿qué sucede si obtengo una Ruta por sí mismo? Cuando buscaba el Camino, ya había comenzado, ¿no? – nambrot

+0

Para encontrar una palabra para esto, creo que lo que quieres hacer es leer más sobre "modelos anidados y colecciones". – jmk2142

Respuesta

24

por lo que he escrito un par de respuestas relacionadas con el uso de parse() y set() para crear instancias y poblar submodelos y sub-colecciones (datos anidados). Sin embargo, no he visto una respuesta realmente completa que consolide algunas de las muchas prácticas que he visto. Tiendo a dar vueltas un poco cuando escribo mucho, así que podría desviar un poco, pero esto podría ser útil para las personas que están pasando por un tipo similar de problemas.

Hay varias formas de hacerlo. Usar el parse() es uno. Manipular el set() es otro. Hacer una instancia de estos en su initialize() es otro. Hacerlo todo fuera del modelo Path es otro (por ejemplo, path = new Path(); path.nodes = new NodeCollection(); etc.)

Una segunda consideración es esta. ¿Desea que los nodos y las colecciones de borde sean atributos de modelo? O propiedades del modelo?

¡Oh tantas opciones! Mucha libertad, pero a veces (para nuestra frustración) hace que sea más difícil determinar el "camino correcto".

Dado que esto aparece con frecuencia, voy a hacer una publicación larga y revisar estos uno por uno. Así que tengan paciencia conmigo mientras continúo actualizando esta respuesta.

Hacerlo fuera de los modelos - sencilla y directa

Esto es por lo general una forma fácil de añadir modelos anidados y colecciones cuando sólo lo necesita en un modelo o colección particular.

path = new PathModel(); 

path.nodes = new NodeCollection(); 
path.edge = new EdgeCollection(); 

// Continue to set up the nested data URL, etc. 

Esta es la manera más simple y funciona bien cuando se está tratando con uno modelos de tiempo y colecciones que no es necesario tener una definición. Aunque podría producir fácilmente estos modelos en algún método (por ejemplo, el método de vista) que construye este objeto antes de hacer cualquier cosa con él.

Usando initialize() Sub-modelo/colecciones en cada modelo

Si sabe que todas las instancias de un determinado modelo tendrá siempre un sub-modelo o sub-colección, la forma más fácil de preparar las cosas se utilizar la función initialize().

Por ejemplo, tome su modelo de trayectoria:

Path = Backbone.Model.extend({ 
    initialize: function() { 
     this.nodes = new NodeCollection(); 
     this.paths = new PathCollection(); 

     // Maybe assign a proper url in relation to this Path model 
     // You might even set up a change:id listener to set the url when this 
     // model gets an id, assuming it doesn't have one at start. 
     this.nodes.url = this.id ? 'path/' + this.id + '/nodes' : undefined; 
     this.paths.url = this.id ? 'path/' + this.id + '/paths' : undefined; 
    } 
}); 

Ahora sus sub-colecciones se pueden recuperar como path.nodes.fetch() y se encaminará a la URL correcta. Pan comido.

parse() El uso de instanciar y el establecimiento de sub-datos

Tal vez, se pone un poco más complicado si no desea asumir cada modelo va a tener unos nodos y colecciones de vanguardia. Tal vez quiera modelos/colecciones anidados solo si el fetch() devuelve esos datos. Este es el caso donde usar parse() puede ser útil.

Lo que pasa con parse() es que se necesita CUALQUIER respuesta del servidor json y se puede asignar espacio de nombre y tratarlo adecuadamente antes de pasarlo a la función de modelo set(). Por lo tanto, podemos verificar si se incluye un modelo o datos sin procesar de la colección y manejarlos antes de reducir la respuesta a los atributos del modelo principal.

Por ejemplo, tal vez de nuestro servidor obtenemos esta respuesta:

// Path model JSON example with nested collections JSON arrays 
{ 
    'name':'orange site', 
    'url':'orange.com', 
    'nodes':[ 
     {'id':'1', 'nodeColor':'red'}, 
     {'id':'2', 'nodeColor':'white'}, 
     {'id':'3', 'nodeColor':'blue'} 
    ], 
    'edge':[ 
     {'id':'1', 'location':'north'}, 
     {'id':'1', 'location':'south'}, 
     {'id':'1', 'location':'east'} 
    ] 
} 

con la cadena principal por defecto parse() engullirá esto y asignar sus Atributos de ruta modelo 'nodos' y 'borde' con un array() de datos (no colecciones). Por lo tanto, queremos asegurarnos de que nuestro parse() se ocupe de esto adecuadamente.

parse: function(response) { 

    // Check if response includes some nested collection data... our case 'nodes' 
    if (_.has(response, 'nodes')){ 

     // Check if this model has a property called nodes 
     if (!_.has(this, 'nodes')) { // It does not... 
      // So instantiate a collection and pass in raw data 
      this.nodes = new NodeCollection(response.nodes); 
     } else { 
      // It does, so just reset the collection 
      this.nodes.reset(response.nodes); 
     } 

     // Assuming the fetch gets this model id 
     this.nodes.url = 'path/' + response.id + '/nodes'; // Set model relative URL 

     // Delete the nodes so it doesn't clutter our model attributes 
     delete response.nodes; 
    } 

    // Same for edge... 

    return response; 
} 

También puede utilizar un set() personalizado para hacer frente a sus sub-datos. Después de mucho ir y venir entre lo que es mejor, manipular set() o hacerlo en parse() he decidido que me gusta usar parse() más. Pero estoy abierto a los pensamientos de otras personas sobre esto.

Usando set() para hacer frente a sus sub-datos

Mientras parse() se basa en cualquiera de ir a buscar los datos o pasar datos a una colección con la opción parse:true algunas personas les resulta preferente para cambiar la función set(). De nuevo, no estoy seguro de que haya una opción correcta, pero aquí es cómo funcionaría.

set: function(attributes, options) { 
    // If we pass in nodes collection JSON array and this model has a nodes attribute 
    // Assume we already set it as a collection 
    if (_.has(attributes, 'nodes') && this.get("nodes")) { 
     this.get('nodes').reset(attributes.nodes); 
     delete attributes.nodes; 
    } else if (_.has(attributes, 'nodes') && !this.get('nodes')) { 
     this.set('nodes', new NodeCollection(attributes.nodes)); 
     delete attributes.nodes; 
    } 

    return Backbone.Model.prototype.set.call(this, attributes, options); 
} 

Así que si ya tenemos un atributo y es una colección, que se reset(). Si tenemos un atributo pero no es una colección, lo instanciamos. Es importante asegurarse de que usted traduce correctamente la matriz JSON de sub-datos en una colección antes de pasarlo al prototipo set(). Backbone, no interpreta la matriz JSON como una colección y solo obtendrá una matriz vertical.

Por lo tanto, en pocas palabras, tiene muchas opciones sobre cómo hacerlo. De nuevo, actualmente estoy a favor de una combinación de usar initialize() cuando sé que algo siempre tendrá esos submodelos/colecciones y parse() cuando la situación solo requiera posibles datos anidados en las llamadas fetch().

Con respecto a su pregunta ...(Ah, sí, había una pregunta)

Puede permitir que Path cree instancias de los submodelos de un hash en una variedad de formas. Acabo de darle 4. PUEDE usar el análisis si lo desea, si sabe que va a ser fetch() el modelo de ruta O tal vez incluso un pathCollection ... pathCollection.fetch({parse:true}) ¿Hay alguna convención? Tal vez tal vez no. Me gusta usar una combinación de formas dependiendo del contexto en el que creo que usaré los modelos/colecciones.

Estoy muy abierto a la discusión sobre algunas de estas prácticas y si son buenas o malas. Son solo muchas soluciones que he encontrado en Stack e incorporado a mis propios hábitos de trabajo y parecen funcionar bien para mí. :-)

Tómate un café y una palmadita en la espalda, eso fue una lectura larga.

Cuestiones relacionadas