javascript - template - Asignación de colecciones JSON a backbone.js
backbonejs github (3)
De acuerdo, parece que necesito una pista para apuntarme en la dirección correcta. Esta pregunta tiene dos partes: trabajar con JSON multidimensional y Colecciones de colecciones de JSON.
Fondo
Tengo un JSON que se recuperará de un servidor y tengo control sobre cómo se puede formatear.
JSON multidimensional
Tengo problemas para conectar el modelo a las partes en el JSON. Digamos que quería representar solo cada uno de los posts nombre del autor , y el contenido del estado en la muestra JSON a continuación. No tengo problemas para obtener el estado en el modelo, pero el nombre del autor me da un poco de confusión sobre cómo llegar a él. Según tengo entendido, tengo que anular el análisis.
¿Son estos malos estándares / hay una mejor estructura JSON que debería usar? ¿Sería mejor mantenerlo lo más plano posible? ¿Eso es mover el nombre del autor y la foto a un nivel?
Estaba leyendo Cómo crear una Colección / Modelo desde JSON anidado con Backbone.js, pero aún no me queda claro.
Colección en Colecciones
¿Hay una buena manera de hacer una colección dentro de una colección para backbone.js? Tendré una colección de publicaciones y luego tendré una colección de comentarios sobre esa publicación. Como estoy desarrollando en la columna vertebral, ¿es eso posible?
Por lo que entiendo en Backbone.js Collection of Collections y Backbone.js Collection of Collections Issue , ¿se vería algo así?
var Comments = Backbone.Model.extend({
defaults : {
_id : "",
text : "",
author : ""
}
})
var CommentsCollection = Backbone.Collection.extend({ model : Comments })
var Posts = Backbone.Model.extend({
defaults : {
_id : "",
author : "",
status : "",
comments : new CommentsCollection
}
})
var PostsCollection = Backbone.Collection.extend({ model : Posts })
Muestra JSON
{
"posts" : [
{
"_id": "50f5f5d4014e045f000002",
"author": {
"name" : "Chris Crawford",
"photo" : "http://example.com/photo.jpg"
},
"status": "This is a sample message.",
"comments": [
{
"_id": "5160eacbe4b020ec56a46844",
"text": "This is the content of the comment.",
"author": "Bob Hope"
},
{
"_id": "5160eacbe4b020ec56a46845",
"text": "This is the content of the comment.",
"author": "Bob Hope"
},
{
...
}
]
},
{
"_id": "50f5f5d4014e045f000003",
"author": {
"name" : "Chris Crawford",
"photo" : "http://example.com/photo.jpg"
},
"status": "This is another sample message.",
"comments": [
{
"_id": "5160eacbe4b020ec56a46846",
"text": "This is the content of the comment.",
"author": "Bob Hope"
},
{
"_id": "5160eacbe4b020ec56a46847",
"text": "This is the content of the comment.",
"author": "Bob Hope"
},
{
...
}
]
},
{
...
}
]}
Agradezco incluso cualquier pista para guildme. ¡Gracias!
Puede ser abrumador cuando intenta escribir código para hacerlo funcionar para objetos anidados. Pero para hacerlo más simple, dividamos en partes manejables más pequeñas.
Yo pensaría en estas líneas.
Colecciones
Posts
Comments
Modelos
Post
Comment
Author
Main collection -- Posts collection
(Which contains list of Post Models)
Y each model in the Posts collection
tendrá 3 conjuntos de atributos (puede que no sea el término correcto).
1º - nivel de atributos (estado, id).
2º - Atributo de autor que puede colocarse en un Modelo separado (Modelo de autorización).
3º - Colección de comentarios para cada modelo de publicación.
La colección en Colecciones sería un poco confusa aquí. Como tendrías Modelos en colección ( Post Model inside Posts Collection
Colección de Post Model inside Posts Collection
) y cada Modelo anidará una colección nuevamente ( Comments collection inside Post Model
). Básicamente, estarías manejando una Collection inside a Model
.
Según tengo entendido, tengo que anular el análisis.
¿Son estos malos estándares / hay una mejor estructura JSON que debería usar?
Es una solución perfectamente plausible para manejar el procesamiento esto en el método Parse. Cuando inicializa una Colección o un Modelo, primero se llama a los métodos Parse y luego se invoca. Por lo tanto, es perfectamente lógico manejar la lógica dentro del método Parse y no es en absoluto un mal estándar.
¿Sería mejor mantenerlo lo más plano posible?
No creo que sea una buena idea mantener este plano en un solo nivel, ya que los otros datos no son necesarios en el primer nivel en primer lugar.
Entonces, la forma en que abordaría este problema es escribir el método de parse
en el Post Model
que procesa la respuesta y adjuntar el modelo de autor y la colección de comentarios directamente en el modelo como un atributo en el modelo para mantener los atributos hash clean consistente en 1ra. nivel de datos de publicación. Esto creo que será más limpio y mucho más escalable a largo plazo.
var postsObject = [{
"_id": "50f5f5d4014e045f000002",
"author": {
"name": "Chris Crawford",
"photo": "http://example.com/photo.jpg"
},
"status": "This is a sample message.",
"comments": [{
"_id": "5160eacbe4b020ec56a46844",
"text": "This is the content of the comment.",
"author": "Bob Hope"
}, {
"_id": "5160eacbe4b020ec56a46845",
"text": "This is the content of the comment.",
"author": "Bob Hope"
}]
}, {
"_id": "50f5f5d4014e045f000003",
"author": {
"name": "Brown Robert",
"photo": "http://example.com/photo.jpg"
},
"status": "This is another sample message.",
"comments": [{
"_id": "5160eacbe4b020ec56a46846",
"text": "This is the content of the comment.",
"author": "Bob Hope"
}, {
"_id": "5160eacbe4b020ec56a46847",
"text": "This is the content of the comment.",
"author": "Bob Hope"
}]
}];
// Comment Model
var Comment = Backbone.Model.extend({
idAttribute: ''_id'',
defaults: {
text: "",
author: ""
}
});
// Comments collection
var Comments = Backbone.Collection.extend({
model: Comment
});
// Author Model
var Author = Backbone.Model.extend({
defaults: {
text: "",
author: ""
}
});
// Post Model
var Post = Backbone.Model.extend({
idAttribute: ''_id'',
defaults: {
author: "",
status: ""
},
parse: function (resp) {
// Create a Author model on the Post Model
this.author = new Author(resp.author || null, {
parse: true
});
// Delete from the response object as the data is
// alredy available on the model
delete resp.author;
// Create a comments objecton model
// that will hold the comments collection
this.comments = new Comments(resp.comments || null, {
parse: true
});
// Delete from the response object as the data is
// alredy available on the model
delete resp.comments;
// return the response object
return resp;
}
})
// Posts Collection
var Posts = Backbone.Collection.extend({
model: Post
});
var PostsListView = Backbone.View.extend({
el: "#container",
renderPostView: function(post) {
// Create a new postView
var postView = new PostView({
model : post
});
// Append it to the container
this.$el.append(postView.el);
postView.render();
},
render: function () {
var thisView = this;
// Iterate over each post Model
_.each(this.collection.models, function (post) {
// Call the renderPostView method
thisView.renderPostView(post);
});
}
});
var PostView = Backbone.View.extend({
className: "post",
template: _.template($("#post-template").html()),
renderComments: function() {
var commentsListView = new CommentsListView({
// Comments collection on the Post Model
collection : this.model.comments,
// Pass the container to which it is to be appended
el : $(''.comments'', this.$el)
});
commentsListView.render();
},
render: function () {
this.$el.empty();
// Extend the object toi contain both Post attributes
// and also the author attributes
this.$el.append(this.template(_.extend(this.model.toJSON(),
this.model.author.toJSON()
)));
// Render the comments for each Post
this.renderComments();
}
});
var CommentsListView = Backbone.View.extend({
renderCommentView: function(comment) {
// Create a new CommentView
var commentView = new CommentView({
model : comment
});
// Append it to the comments ul that is part
// of the view
this.$el.append(commentView.el);
commentView.render();
},
render: function () {
var thisView = this;
// Iterate over each Comment Model
_.each(this.collection.models, function (comment) {
// Call the renderCommentView method
thisView.renderCommentView(comment);
});
}
});
var CommentView = Backbone.View.extend({
tagName: "li",
className: "comment",
template: _.template($("#comment-template").html()),
render: function () {
this.$el.empty();
this.$el.append(this.template(this.model.toJSON()));
}
});
// Create a posts collection
var posts = new Posts(postsObject, {parse: true});
// Pass it to the PostsListView
var postsListView = new PostsListView({
collection: posts
});
// Render the view
postsListView.render();
(Editado para corregir mi error de lectura inicial de la pregunta).
No es necesario anular el método de parse
del modelo a menos que desee cambiar su estructura. Pero parece que no necesita hacerlo: para representar el nombre del autor, simplemente use author.name
en la vista:
<%= author.name %>
En cuanto a la inicialización de la colección anidada, su enfoque es el correcto. Todo lo que tiene que hacer es convertir el objeto JSON en modelos Backbone y pasarlos a PostsCollection
(el constructor Backbone.Collection
acepta una matriz de modelos Backbone, no JSON sin formato). Una forma de hacerlo es usar el map
:
var postModels = json.posts.map(function(post) { return new Posts(post); });
var posts = new PostsCollection(postModels);
Tenga en cuenta que deberá hacer algo similar en el método de initialize
del modelo de Posts
: recuperar la matriz JSON de comentarios y convertirla en una matriz de modelos de Comments
:
initialize: function() {
if (attributes.comments && attributes.comments.length > 0) {
var commentModels = attributes.comments.map(function(comment) {
return new Comments(comment);
});
this.set("comments", new CommentsCollection(commentModels));
}
}
Actualización, encontré un SuperModel para backbone que proporciona relaciones entre modelos y entre colecciones. Ha demostrado ser una gran solución para colecciones dentro de colecciones, así como datos de modelos anidados profundos.
Los modelos están predefinidos con sus relaciones con otros modelos a través de la clave. Durante la inicialización / análisis del modelo, cualquier valor en el JSON de esa clave pasa a un nuevo modelo o colección relacionado. Se crea una relación entre los dos modelos / colecciones.
Esto significa que con el ejemplo anterior podemos hacer algo como esto con nuestros modelos:
Preparar
var Author = Supermodel.Model.extend({});
var Post = Supermodel.Model.extend({});
var Comment = Supermodel.Model.extend({});
var Posts = Backbone.Collection.extend({
model: function(attrs, options) {
return Post.create(attrs, options);
}
});
var Comments = Backbone.Collection.extend({
model: function(attrs, options) {
return Comment.create(attrs, options);
}
});
Post.has().one(''author'', {
model: Author,
inverse: ''post''
}).many(''comments'', {
collection: Comments,
inverse: ''post''
});
//reverse relationships could also be setup
Uso
var posts = new Posts( postsObject ); //where postsObject is an array of posts
//With SuperModel, we are able to navigate the related models
posts.first().comments();
posts.first().comments().author();
posts.last().author();