在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设置视图属性

http://jsfiddle.net/5wd0ma8b/

 // 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 … eltagNameidclassName现在可以定义为函数,如果你想在运行时确定它们的值。

    我提到这种情况下,如果出现这种情况,可以使用所示的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>或者任何其他元素将会破坏内容模型。 您可能需要专注于正确设置视图的元素(使用像tagNameclassNameid类的属性),然后再渲染其内容

如何让你的Backbone视图对象与DOM交互的选项是敞开的。 有两个基本的初始场景:

  • 您可以将现有的DOM元素附加到主干视图。

  • 您可以允许Backbone创build与文档断开的新元素,然后以某种方式将其插入到文档中。

有多种方式可以为元素生成内容(设置文字string,如您的示例;使用模板库,如小胡子,把手等)。 你应该如何使用视图的el属性取决于你在做什么。

现有的元素

您的渲染示例build议您有一个现有的元素,您将分配给视图,虽然您不显示实例化的意见。 如果是这种情况,并且元素已经在文档中,那么你可能想要做这样的事情(更新el的内容,但是不要改变el本身):

 render : function () { this.$el.html( "Some stuff" ); } 

http://jsfiddle.net/vQMa2/1/

生成的元素

假设您没有现有元素,并且允许Backbone为您生成一个元素。 你可能想要做这样的事情(但是最好devise一些东西,这样你的观点就不需要知道外面的事情):

 render : function () { this.$el.html( "Some stuff" ); $( "#some-container" ).append( this.el ); } 

http://jsfiddle.net/vQMa2/

模板

就我而言,我正在使用模板,例如:

 <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'); } }); 

您可以在根元素上设置属性classNameid : 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属性,这意味着如果classNameid是一个函数,它将被调用,否则将使用它的值。

所以你可以直接在构造函数中给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 }); } } });