我正在学习和使用Backbone.js。
我有一个Item模型和一个相应的Item视图。 每个模型实例都有item_class和item_id属性,我希望将它们反映为相应视图的“id”和“class”属性。 实现这一目标的正确方法是什么?
示例:
var ItemModel = Backbone.Model.extend({
});
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
var ItemView = Backbone.View.extend({
});
我应该如何实现视图,以便视图'el'将转换为:
<div id="id1" class="nice"></div>
<div id="id2" class="sad"> </div>
在我看到的大多数示例中,视图的 el 用作无意义的包装元素,在其中必须手动编写“语义”代码。
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
render: function() {
$(this.el).html("<div id="id1" class="nice"> Some stuff </div>");
}
});
所以当渲染时,会得到
<div> <!-- el wrapper -->
<div id="id1" class="nice"> Some stuff </div>
</div>
但这似乎是浪费 - 为什么有外部div?我希望 el 直接翻译成内部div!
答案 0 :(得分:132)
// View class with `attributes` method
var View = Backbone.View.extend( {
attributes : function () {
// Return model data
return {
class : this.model.get( 'item_class' ),
id : this.model.get( 'item_id' )
};
}
// attributes
} );
// Pass model to view constructor
var item = new View( {
model : new Backbone.Model( {
item_class : "nice",
item_id : "id1"
} )
} );
此示例假设您允许Backbone为您生成DOM元素。
在设置传递给视图构造函数的属性(在本例中为attributes
)之后调用model
方法,允许您在Backbone之前使用模型数据动态设置属性创建el
。
与其他一些答案形成对比:不在视图类中硬编码属性值,从模型数据中动态设置它们;不等到render()
设置attr vals;在每次调用render()
时都不会重复设置attr vals;没有必要手动设置DOM元素的attr val。
请注意,如果在调用Backbone.View.extend
或视图构造函数(例如new Backbone.View
)时设置类,则必须使用DOM属性名称className
,但是如果设置通过attributes
哈希/方法(如本例所示),您必须使用属性名称class
。
截至Backbone 0.9.9:
当声明视图...
el
时,tagName
,id
和className
现在可以定义为函数,如果您希望在运行时确定它们的值
我提到这种情况,以防有可能作为使用attributes
方法的替代方法。
如果您正在使用现有元素(例如将el
传递给视图构造函数)...
var item = new View( { el : some_el } );
...然后attributes
不会应用于该元素。如果尚未在元素上设置所需的属性,或者您不想在视图类和其他位置复制该数据,那么您可能需要向您的元素添加initialize
方法将attributes
应用于el
的视图构造函数。这样的事情(使用jQuery.attr
):
View.prototype.initialize = function ( options ) {
this.$el.attr( _.result( this, 'attributes' ) );
};
el
的使用,渲染,避免包装在我看到的大多数例子中,视图el用作无意义的包装元素,在其中必须手动编写语义&#39;代码。
没有理由view.el
需要&#34;一个毫无意义的包装元素&#34;。实际上,这通常会打破DOM结构。例如,如果视图类表示<li>
元素,则需要将其呈现为<li>
- 将其呈现为<div>
或任何其他元素将破坏内容模型。您可能希望专注于正确设置视图的元素(使用tagName
,className
和id
等属性),然后渲染其内容此后。
如何让Backbone视图对象与DOM交互的选项是开放的。有两个基本的初始场景:
您可以将现有DOM元素附加到Backbone视图。
您可以允许Backbone创建一个与文档断开连接的新元素,然后以某种方式将其插入到文档中。
您可以通过多种方式为元素生成内容(设置文字字符串,如示例中所示;使用模拟库,如Mustache,Handlebars等)。如何使用视图的el
属性取决于您正在做什么。
您的渲染示例表明您有一个现有的元素,您可以将其分配给视图,但您不会显示视图的实例化。如果是这种情况,并且元素已经在文档中,那么您可能希望执行以下操作(更新el
的内容,但不要更改el
本身):
render : function () {
this.$el.html( "Some stuff" );
}
我们假设您没有现有元素,并允许Backbone为您生成一个元素。你可能想要做这样的事情(但是建立事物可能更好,这样你的观点就不负责了解自己以外的任何事情):
render : function () {
this.$el.html( "Some stuff" );
$( "#some-container" ).append( this.el );
}
在我的情况下,我使用模板,例如:
<div class="player" id="{{id}}">
<input name="name" value="{{name}}" />
<input name="score" value="{{score}}" />
</div>
<!-- .player -->
模板代表完整视图。换句话说,模板周围没有包装器 - div.player
将是我视图的根或最外层元素。
我的播放器类看起来像这样(非常简单的render()
示例):
Backbone.View.extend( {
tagName : 'div',
className : 'player',
attributes : function () {
return {
id : "player-" + this.model.cid
};
},
// attributes
render : function {
var rendered_template = $( ... );
// Note that since the top level element in my template (and therefore
// in `rendered_template`) represents the same element as `this.el`, I'm
// extracting the content of `rendered_template`'s top level element and
// replacing the content of `this.el` with that.
this.$el.empty().append( rendered_template.children() );
}
} );
答案 1 :(得分:94)
在你看来,只需做这样的事情
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
render: function() {
$(this.el).attr('id', 'id1').addClass('nice').html('Some Stuff');
}
});
答案 2 :(得分:27)
您可以在根元素上设置属性className
和id
:
http://documentcloud.github.com/backbone/#View-extend
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
className : 'nice',
id : 'id1',
render: function() {
$(this.el).html("Some stuff");
}
});
编辑包含基于构造函数参数设置ID的示例
如果视图的构造如上所述:
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
然后可以这样设置值:
// ...
className: function(){
return this.options.item_class;
},
id: function(){
return this.options.item_id;
}
// ...
答案 3 :(得分:6)
我知道这是一个老问题,但补充说明。在新的骨干版本中,这似乎更容易。在Backbone 1.1中,使用下划线ensureElement
在函数_.result
(参见from source)中评估id和className属性,这意味着如果className
或id
是函数,它将被调用,否则将使用它的值。
所以你可以直接在构造函数中给出className,给出另一个将在className中使用的参数等等......很多选项
所以这应该有用
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
var ItemView = Backbone.View.extend({
id: function() { return this.model.get('item_id'); },
className: function() { return this.model.get('item_class'); }
});
答案 4 :(得分:4)
其他示例未显示如何从模型中实际获取数据。要从模型的数据中动态添加id和class:
var ItemView = Backbone.View.extend({
tagName: "div",
render: function() {
this.id = this.model.get('item_id');
this.class = this.model.get('item_class');
$(this.el).attr('id',this.id).addClass(this.class).html('Some Stuff');
}
});
答案 5 :(得分:2)
您需要删除tagName并声明el。
'tagName'表示您希望骨干创建元素。如果元素已存在于DOM中,则可以指定类似:
el: $('#emotions'),
以后:
render: function() {
$(this.el).append(this.model.toJSON());
}
答案 6 :(得分:1)
尝试在initialize方法中分配值,这将直接将id和class动态分配给div属性。
var ItemView = Backbone.View.extend( {
tagName : "div",
id : '',
class : '',
initialize : function( options ) {
if ( ! _.isUndefined( options ) ) {
this.id = options.item_id;
this.class= options.item_class;
}
},
render : function() {
$( this.el ).html( this.template( "stuff goes here" ) );
}
} );
答案 7 :(得分:0)
这是通过模型动态更改视图元素类的最小方法,并在模型更改时更新它。
var VMenuTabItem = Backbone.View.extend({
tagName: 'li',
events: {
'click': 'onClick'
},
initialize: function(options) {
// auto render on change of the class.
// Useful if parent view changes this model (e.g. via a collection)
this.listenTo(this.model, 'change:active', this.render);
},
render: function() {
// toggle a class only if the attribute is set.
this.$el.toggleClass('active', Boolean(this.model.get('active')));
this.$el.toggleClass('empty', Boolean(this.model.get('empty')));
return this;
},
onClicked: function(e) {
if (!this.model.get('empty')) {
// optional: notify our parents of the click
this.model.trigger('tab:click', this.model);
// then update the model, which triggers a render.
this.model.set({ active: true });
}
}
});