在Backbone.js视图中dynamic设置id和className
我正在学习和使用Backbone.js。
我有一个项目模型和相应的项目视图。 每个模型实例都具有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({ });
我应该如何执行这个观点,以便将这个观点的意见转化为:
<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>
但这似乎是一个浪费 – 为什么有外部股利? 我想el直接翻译成内部分区!
总结:使用模型数据dynamic设置视图属性
// 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元素。
-
在传递给视图构造函数的属性被设置(在这种情况下,
model
)之后,将调用attributes
方法,允许您在Backbone创buildel
之前使用模型数据dynamic设置属性。 -
与其他一些答案相反:在视图类中不硬编码属性值,根据模型数据dynamic设置它们; 不等到
render()
设置attr vals; 每次调用render()
都不会重复设置属性值。 不会不必要地手动设置DOM元素的属性值。 -
请注意,如果在调用
Backbone.View.extend
或视图构造函数(如new Backbone.View
)时设置类,则必须使用DOM属性名称className
,但是如果通过attributes
hash / method进行设置(如例如)你必须使用属性名称,class
。 -
从骨干0.9.9开始:
当声明一个View …
el
,tagName
,id
和className
现在可以定义为函数,如果你想在运行时确定它们的值。我提到这种情况下,如果出现这种情况,可以使用所示的
attributes
方法作为替代scheme。
使用现有的元素
如果你正在使用一个现有的元素(例如传递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是一个无意义的包装元素,在这个元素里面必须手动编写“语义”代码。
没有理由view.el
需要是“一个毫无意义的包装元素”。 实际上,这往往会破坏DOM结构。 例如,如果一个视图类表示一个<li>
元素,那么它需要被渲染为一个<li>
– 将其渲染为一个<div>
或者任何其他元素将会破坏内容模型。 您可能需要专注于正确设置视图的元素(使用像tagName
, className
和id
类的属性),然后再渲染其内容 。
如何让你的Backbone视图对象与DOM交互的选项是敞开的。 有两个基本的初始场景:
-
您可以将现有的DOM元素附加到主干视图。
-
您可以允许Backbone创build与文档断开的新元素,然后以某种方式将其插入到文档中。
有多种方式可以为元素生成内容(设置文字string,如您的示例;使用模板库,如小胡子,把手等)。 你应该如何使用视图的el
属性取决于你在做什么。
现有的元素
您的渲染示例build议您有一个现有的元素,您将分配给视图,虽然您不显示实例化的意见。 如果是这种情况,并且元素已经在文档中,那么你可能想要做这样的事情(更新el
的内容,但是不要改变el
本身):
render : function () { this.$el.html( "Some stuff" ); }
生成的元素
假设您没有现有元素,并且允许Backbone为您生成一个元素。 你可能想要做这样的事情(但是最好devise一些东西,这样你的观点就不需要知道外面的事情):
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() ); } } );
在你看来,只是做这样的事情
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'); } });
您可以在根元素上设置属性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的示例
如果这些观点是按照上述build构的话:
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; } // ...
我知道这是一个古老的问题,但添加了参考。 新主干版本似乎更容易。 在Backbone 1.1中,使用下划线_.result
在函数ensureElement
(参见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'); } });
其他的例子并没有显示如何真正从模型中获取数据。 从模型的数据dynamic添加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'); } });
你需要删除tagName并声明一个el。
'tagName'表示你想要骨干来创build一个元素。 如果该元素已经存在于DOM中,则可以指定一个el,如:
el: $('#emotions'),
然后:
render: function() { $(this.el).append(this.model.toJSON()); }
尝试在初始化方法中分配值,这将dynamic地直接将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" ) ); } } );
下面是通过模型dynamic改变视图元素类的最简单的方法,并根据模型更改进行更新。
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 (eg 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 }); } } });