关于javascript:将JSON映射到backbone.js集合

Mapping JSON to backbone.js collections

好吧,看来我需要一个提示来指引我正确的方向。这个问题分为两部分 - 使用多维 JSON 和来自 JSON 的集合。

背景

我有一些 JSON 将从服务器中检索出来,并且可以控制它的格式。

多维 JSON

我无法将模型连接到 JSON 中的部件。假设我只想呈现每个帖子的作者姓名,以及下面示例 JSON 中的状态内容。我将状态输入模型没有问题,但作者姓名我有点困惑如何获得它。据我了解,我必须覆盖解析。

这是不好的标准/我应该使用更好的 JSON 结构吗?保持它尽可能平坦会更好吗?那就是将作者姓名和照片上移一级?

我正在阅读如何使用 Backbone.js 从嵌套 JSON 构建集合/模型,但对我来说仍然有点不清楚。

收藏中的收藏

有没有一种很好的方法可以在backbone.js 的集合中创建一个集合?我将收集一系列帖子,然后收集关于该帖子的评论。当我在Backbone 中发展时,这甚至可能吗?

根据我在 Backbone.js Collection of Collections 和 Backbone.js Collection of Collections 问题中的理解,它看起来像这样?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 })

示例 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
"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"
                },
                {
                ...
                }
        ]
    },
    {
    ...
    }
]}

我什至感谢任何可以引导我的提示。谢谢!


在尝试编写代码以使其适用于嵌套对象时,这可能会让人不知所措。但为了使其更简单,让我们将其分解为更小的可管理部分。

我会这么想。

收藏

1
2
 Posts
 Comments

型号

1
2
3
 Post
 Comment
 Author
1
2
Main collection --  Posts collection
                    (Which contains list of Post Models)

each model in the Posts collection 将有 3 组属性(可能不是正确的术语)。

第一级属性(状态,id)。

2nd - Author 属性,可以放置在单独的模型(Authod 模型)中。

3rd - 每个帖子模型的评论集合。

Collection in Collections 在这里会有点混乱。
就像您在 Collection(Post Model inside Posts Collection) 中拥有模型一样,每个模型将再次嵌套一个集合 (Comments collection inside Post Model)。基本上你会处理一个Collection inside a Model

From my understanding I have to override the parse.

Is this bad standards / is there a better JSON structure I should use?

在 Parse 方法中处理 this 是一个完全合理的解决方案。初始化 Collection 或 Model 时,首先调用 Parse 方法,然后调用 initialize。因此,处理 Parse 方法内部的逻辑是完全合乎逻辑的,这根本不是一个糟糕的标准。

Would it be better to keep it as flat as possible?

我认为将这个平面保持在一个级别不是一个好主意,因为第一级不需要其他数据。

所以我解决这个问题的方法是在 Post Model 中编写 parse 方法,该方法处理响应并将作者模型和评论集合直接附加到模型上,而不是作为模型上的属性来保持属性散列清理由第一级 Post 数据组成。从长远来看,我觉得这会更干净,更具可扩展性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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();

检查小提琴


(已编辑以纠正我最初对该问题的误读。)

除非你想改变它的结构,否则不需要重写模型的 parse 方法。但听起来你不需要 -- 渲染作者姓名,只需在视图中使用 author.name 即可:

1
<%= author.name %>

就初始化嵌套集合而言,您的方法是完全正确的。您所要做的就是将 JSON 对象转换为 Backbone 模型,并将它们传递给 PostsCollection(Backbone.Collection 构造函数接受 Backbone 模型数组,而不是原始 JSON)。一种方法是使用 map:

1
2
var postModels = json.posts.map(function(post) { return new Posts(post); });
var posts = new PostsCollection(postModels);

请注意,您需要在 Posts 模型的 initialize 方法中执行类似的操作——检索评论 JSON 数组,并将其转换为 Comments 模型数组:

1
2
3
4
5
6
7
8
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));
    }
}

这是工作示例。


更新,我找到了一个用于Backbone 网的 SuperModel,它提供了模型之间和集合之间的关系。事实证明,它是集合内的集合以及深度嵌套模型数据的绝佳解决方案。

模型是预先定义的,它们通过键与其他模型的关系。在模型的初始化/解析期间,该键处的 JSON 中的任何值都会传递给新的相关模型或集合。在两个模型/集合之间创建关系。

这意味着在上面的例子中我们可以对我们的模型做这样的事情:

设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

用法

1
2
3
4
5
6
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();

小提琴

JSFiddle 中的工作示例