在Backbone.js视图中动态设置id和className


85

我正在学习和使用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!

Answers:


133

简介:使用模型数据动态设置视图属性

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元素。

  • attributes设置传递给视图构造函数的属性(在本例中为model)之后,将调用此方法,从而允许您在Backbone创建之前使用模型数据动态设置属性el

  • 与其他一些答案相反:不对视图类中的属性值进行硬编码,而是根据模型数据动态设置它们;不要等到render()设置属性值;不会在每次调用时重复设置attr valrender(); 不必在DOM元素上手动设置属性。

  • 请注意,如果在调用时设置类Backbone.View.extend或使用视图构造函数(例如new Backbone.View),则必须使用DOM属性名称className,但是,如果通过attributeshash /方法设置它(如本例所示),则必须使用属性名称class

  • 从主干网0.9.9开始:

    在声明查看...... eltagNameid并且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充当无意义的包装器元素,在其中必须手动编写“语义”代码。

没有理由view.el需要“无意义的包装器元素”。实际上,这通常会破坏DOM结构。<li>例如,如果视图类表示元素,则需要将其呈现为<li>-将其呈现为,<div>否则任何其他元素都会破坏内容模型。您可能需要集中精力正确设置视图的元素(使用,和等属性tagName,然后呈现其内容classNameid

如何使您的Backbone视图对象与DOM交互的选项已广泛使用。有两种基本的初始方案:

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

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

您可以通过多种方式生成元素的内容(如示例所示,设置文字字符串;使用模板库(例如Mustache,Handlebars等))。您应该如何使用el视图属性取决于您在做什么。

现有元素

您的渲染示例建议您有一个现有元素要分配给视图,尽管您没有显示视图的实例化。如果是这种情况,并且该元素已经在文档中,那么您可能需要执行以下操作(更新的内容el,但不要更改el自身):

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

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

生成的元素

假设您没有现有元素,而您允许Backbone为您生成一个元素。您可能想要做这样的事情(但是最好设计一些东西,以便您的视图不负责了解其外部的任何内容):

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

用函数覆盖attributes属性并再次返回对象的绝妙方法!
凯尔(Kel)2012年

2
@Kel是的,这是完成诸如问题所要求的动态操作的好方法,用模型数据填充属性,而不必在实例化视图的地方使用重复代码。您可能知道这一点,但以防万一,这是Backbone的功能,您可以使用一个函数来返回哈希值作为的值attributes,例如可以作为函数或其他一些形式提供的许多其他Backbone属性值的类型。在这些情况下,Backbone会检查该值是否为函数,然后调用它并使用返回值。
JMM 2012年

95

在您看来只需执行以下操作

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

这是正确的回答这个问题,它应该被接受
reach4thelasers

13
该答案没有演示基于模型数据动态设置视图属性,仅显示了对属性值进行硬编码的另一种方法。
JMM 2012年

3
@JMM-他的示例代码也不使用模型数据。此答案基于他的示例代码。显然可以用模型数据代替这些值。
克林特(Clint)2012年

5
@Clint,我不会指望对OP很明显。“他的示例代码也不使用模型数据。” -那是因为他不知道如何,因此不知道为什么寻求帮助。在我看来,他似乎在问如何使用模型数据设置view.el的属性,却不知道该如何处理。答案甚至都没有显示该怎么做,为什么您要等到渲染仍要进行渲染,还是每次渲染都再次进行渲染?“此答案有效...”-它如何起作用?像这样创建的每个视图都将具有相同的属性。它显示的唯一内容是如何避免包装器。
JMM 2012年

自12年2月以来,OP已消失。:(这里的另一个+1这个答案。
阿尔莫

27

您可以设置属性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的示例

如果视图是按上述方式构造的:

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
我觉得这个答案不正确,因为那样每个人ItemView都会有id: 'id1'。这必须在执行时根据中计算model.id
fguillen 2012年

当然,您可以根据需要设置ID。使用函数,变量或其他任何东西。我的代码仅包含一个示例,指出了如何在根元素上设置值。
约尔根2012年

我添加了一个示例,阐明了如何根据构造函数参数动态设置值。
约尔根

这是正确的答案。它正确使用了Backbone功能来解决该问题。
Marc-Antoine Lemieux 2014年

6

我知道这是一个古老的问题,但添加以供参考。在新的主干版本中,这似乎更容易。在Backbone 1.1中,id和className属性在函数中使用下划线评估ensureElement(请参阅参考资料),下划线_.result表示如果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'); }
});

您的示例无效,您想要id: function() { return this.model.get('item_id'); })
Cobby 2014年

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

是“ this.className”还是“ this.class”?
Gabe Rainbow 2013年

2

您需要删除tagName并声明一个el。

'tagName'表示您希望主干创建一个元素。如果DOM中已经存在该元素,则可以指定一个el,例如:

el: $('#emotions'),

然后:

render: function() { 
     $(this.el).append(this.model.toJSON());
}

2

尝试在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" ) ); 
    }
} );

@Michel Pleasae去throught本文档,backbonejs.org/#View-constructor
Hemanth

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 });
        }
    }
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.