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.
¿Ha intentado utilizar el método [initialize] (http://documentcloud.github.com/backbone/#Model-constructor)? – Jack
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
Para encontrar una palabra para esto, creo que lo que quieres hacer es leer más sobre "modelos anidados y colecciones". – jmk2142