javascript 如何在列表和项目视图中呈现主干集合?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11646402/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I render a Backbone Collection in a List and Item View?
提问by bodokaiser
I am working on a contact bar which renders all contacts of a user in a html list.
我正在处理一个联系人栏,它在 html 列表中呈现用户的所有联系人。
What I have:
我有的:
- UserModel - This is a simple Backbone.Model with username and email
- UserCollection - This is used as the contact list
- ContactsView - This is the ul contact list
- ContactView - This is a single contact model rendered as li
- UserModel - 这是一个带有用户名和电子邮件的简单 Backbone.Model
- UserCollection - 这用作联系人列表
- ContactsView - 这是 ul 联系人列表
- ContactView - 这是呈现为 li 的单个联系人模型
I am currently breaking my head about a solution how (and where) I can fetch my UserCollection and how I pass the single models down to a single ContactView item.
我目前正在思考如何(以及在哪里)获取我的 UserCollection 以及如何将单个模型传递给单个 ContactView 项目的解决方案。
Specific hurdles are:
具体的障碍是:
- Where should I fetch, store the UserCollection
- How do I render the contact list
- How do I render the contact items
- How do I prevent fetch({ success: callback }) from breaking my code structure
- 我应该在哪里获取,存储 UserCollection
- 如何呈现联系人列表
- 如何呈现联系人项目
- 如何防止 fetch({ success: callback }) 破坏我的代码结构
My current code is this:
我目前的代码是这样的:
entrance point:
入口点:
// create a new instance of the contact list view
var view = new ContactsView();
// insert the rendered element of the contact list view in to the dom
$('div.contacts-body').html(view.render().el);
view.fetch({ success: view.loadContacts });
ContactsView:
联系人查看:
define(
['jquery', 'underscore', 'backbone', 'text!templates/conversations/contacts.html', 'collections/users', 'views/conversations/contact'],
function($, _, Backbone, ContactsTemplate, UserCollection, ContactView) {
var ContactsView = Backbone.View.extend({
tagName: "ul",
className: "contacts unstyled",
attributes: "",
// I am feeling uneasy hardcoding the collection into the view
initialize: function() {
this.collection = new UserCollection();
},
// this renders our contact list
// we don't need any template because we just have <ul class="contacts"></ul>
render: function() {
this.$el.html();
return this;
},
// this should render the contact list
// really crappy and unflexible
loadContacts: function() {
this.collection.each(function(contact) {
// create a new contact item, insert the model
var view = new ContactView({ model: contact });
// append it to our list
this.$el.append(view.render().el);
});
}
});
return ContactsView;
});
ContactView
联系人视图
define(
['jquery', 'underscore', 'backbone', 'text!templates/conversations/contact.html'],
function($, _, Backbone, ContactTemplate) {
var ContactView = Backbone.View.extend({
tagName: "li",
className: "contact",
attributes: "",
template:_.template(ContactTemplate),
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
return ContactView;
});
Could somebody help me about my four hurdles.
有人能帮我解决我的四个障碍吗?
Good example links are welcome. I oriented my code style at the todos list unfortunatly the todos list isn't that advanced...
欢迎使用好的示例链接。我将我的代码风格定位在待办事项列表中,不幸的是,待办事项列表并不是那么先进......
UPDATED CODE:
更新代码:
define(
['jquery', 'underscore', 'backbone', 'text!templates/conversations/contacts.html', 'collections/users', 'views/conversations/contact'],
function($, _, Backbone, ContactsTemplate, UserCollection, ContactView) {
var ContactsView = Backbone.View.extend({
tagName: "ul",
className: "contacts unstyled",
attributes: "",
events: {
},
initialize: function() {
this.collection = new UserCollection();
this.collection.on('reset', this.render);
this.collection.fetch();
},
render: function() {
// in chromium console
console.log(this.el); // first: html, second: undefined
console.log(this.$el); // first: html in array, second: undefined
this.$el.empty(); // error on the called that this.$el is undefined
this.collection.each(function(contact) {
var view = new ContactView({ model: contact });
this.$el.append(view.el);
}.bind(this));
return this;
}
});
return ContactsView;
Can it be that reset is triggering this.render twice?
是不是重置会触发 this.render 两次?
采纳答案by jakee
First of all: why do you fetch the view? Backbone views do not have a fetch method..
首先:你为什么要获取视图?主干视图没有 fetch 方法..
1 The correct place to fetch your UserCollection would be inside the view's initialize method:
1 获取 UserCollection 的正确位置是在视图的 initialize 方法中:
initialize: function() { // ContactsView
_.bindAll(this, 'render', 'otherMethodName', ...); // Bind this to all view functions
...
this.collection.on('reset', this.render); // bind the collection reset event to render this view
this.collection.fetch();
...
}
Now you fetch the contacts exactly when you need them. Next step is to render the collection.
现在,您可以在需要时准确获取联系人。下一步是渲染集合。
2 Binding to the reset event makes your loadContacts method obsolete and we can do that in the render function:
2 绑定到重置事件会使您的 loadContacts 方法过时,我们可以在渲染函数中做到这一点:
render: function() {
this.$el.empty(); // clear the element to make sure you don't double your contact view
var self = this; // so you can use this inside the each function
this.collection.each(function(contact) { // iterate through the collection
var contactView = new ContactView({model: contact});
self.$el.append(contactView.el);
});
return this;
}
Now you render your contactlist inside the render method, where it should be done.
现在你在渲染方法中渲染你的联系人列表,它应该在那里完成。
3 The ContactView actually looks good.
3 ContactView 实际上看起来不错。
Just make the item to render itself in the initialize method, so you don't have to make useless calls in the ContactsView's render method and clutter up your code. Also bindAll here as well.
只需在 initialize 方法中使项目自行呈现,这样您就不必在 ContactsView 的呈现方法中进行无用的调用并弄乱您的代码。也在这里 bindAll 。
initialize: function() { // ContactView
_.bindAll(this, 'render', 'otherMethodName', ...);
...
this.render(); // Render in the end of initialize
}
I have no idea what you are asking in here, but I think the best way is not to use success callbacks. The collections and models trigger events whenever something is done to them, so tapping onto them is much more robust and reliable than success callbacks. Check out the catalog of events to learn more.The Wine Cellar tutorialby Christophe Coenraets is has an excellent example of this kind of listview-listitemview arrangement.
我不知道你在这里问什么,但我认为最好的方法是不要使用成功回调。每当对集合和模型执行某些操作时,它们就会触发事件,因此利用它们比成功回调更加健壮和可靠。查看活动目录以了解更多信息。Christophe Coenraets的Wine Cellar 教程是这种 listview-listitemview 排列的一个很好的例子。
Hope this helps!
希望这可以帮助!
UPDATE: Added _.bindAlls to fix the problem with this in a event bound render call. Some info on binding this.
更新:添加 _.bindAlls 以解决事件绑定渲染调用中的问题。关于绑定这个的一些信息。
回答by fguillen
NOTE: all the code is simplified and no tested
注意:所有代码都经过简化且未经测试
When I have all the elements structure defined, as you have, with all the Models, Collections and Views implemented then I implement a Loaderwhich is in charge of trigger the fetching and rendering actions.
当我像你一样定义了所有的元素结构,并实现了所有的模型、集合和视图,然后我实现了一个加载器,它负责触发获取和呈现操作。
First of all I need to expose the classes definition from the outside something like this:
首先,我需要从外部公开类定义,如下所示:
// App.js
var App = {}
// ContactsCollection.js
$(function(){
var App.ContactsCollection = Backbone.Collection.extend({ ... });
});
// ContactsView.js
$(function(){
var App.ContactsView = Backbone.View.extend({ ... });
});
// and so on...
And then I implement what I call the Loader:
然后我实现了我所说的Loader:
// AppLoad.js
$(function(){
// instantiate the collection
var App.contactsCollection = new App.ContactsCollection();
// instantiate the CollectionView and assign the collection to it
var App.contactsView = new App.ContactsView({
el: "div.contacts-body ul",
collection: App.contactsCollection
});
// fetch the collection the contactsView will
// render the content authomatically
App.contactsCollection.fetch();
});
Another changes you have to do is configure the ContactsView
in a way that respond to the changes in the App.contactsCollection
because as the fetch()
is asynchronous you can call render()
when the collection is still not loaded, so you have to tell to the CollectionView to render it self when the Collection is ready:
您必须做的另一项更改是以ContactsView
响应更改的方式配置 的,App.contactsCollection
因为由于fetch()
是异步的,您可以render()
在尚未加载集合时调用,因此您必须告诉 CollectionView 在集合时自行呈现它准备好了:
var ContactsView = Backbone.View.extend({
initialize: function( opts ){
this.collection.on( 'reset', this.addAll, this );
this.collection.on( 'add', this.addOne, this );
// ... same with 'remove'
},
addOne: function( model ){
var view = new App.ContactView({ model: contact });
this.$el.append( view.render().el );
},
addAll: function(){
this.collection.each( $.proxy( this.addOne, this ) );
}
});
You have to requireyour js files in the proper order:
你必须以正确的顺序要求你的 js 文件:
- App.js
- Your Models, Collections, Views
- AppLoad.js
- 应用程序.js
- 您的模型、集合、视图
- 应用加载.js
With this system you obtain:
使用此系统,您将获得:
- External access to your collection in case you need to access it from another place.
- External control of the
CollectionView.el
with is better for decoupling and testing. - The CollectionView will respond to changes in the Collection authomatically
- 外部访问您的收藏,以防您需要从其他地方访问它。
- 与外部控制
CollectionView.el
更好的解耦和测试。 - CollectionView 将自动响应 Collection 中的更改
Note:If you use Router
you can move the AppLoad.js
logic to there.
注意:如果您使用,Router
您可以将AppLoad.js
逻辑移到那里。