Автоматическое определение id и className в образах Backbone.js

Я изучаю и использую 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 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!

Ответ 1

Сводка: динамически задавать атрибуты вида с данными модели

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 vals; не многократно устанавливает attr vals при каждом вызове render(); необязательно вручную устанавливает attr vals на элемент DOM.

  • Обратите внимание, что если вы устанавливаете класс при вызове Backbone.View.extend или конструкторе представления (например, new Backbone.View), вы должны использовать имя свойства DOM, className, но если установить его с помощью attributes hash/method (как в этом примере), вы должны использовать имя атрибута class.

  • В соответствии с базой 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, рендеринг, исключение оболочки

В большинстве примеров, которые я видел, view el служит бессмысленным элементом оболочки, внутри которого нужно вручную написать "семантический" код.

Нет причины view.el должен быть "бессмысленным элементом оболочки". Фактически, это часто нарушает структуру DOM. Если класс представления представляет собой элемент <li>, например, он должен быть отображен как <li> - рендеринг его как <div> или любой другой элемент разбил бы модель содержимого. Скорее всего, вы захотите сосредоточиться на правильной настройке своего элемента вида (используя такие свойства, как tagName, className и id), а затем после этого передавая его содержимое.

Параметры того, как объекты просмотра Backbone взаимодействуют с DOM, широко открыты. Существует 2 основных исходных сценария:

  • Вы можете прикрепить существующий элемент DOM к представлению Backbone.

  • Вы можете разрешить 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` top level element and
    // replacing the content of `this.el` with that.
    this.$el.empty().append( rendered_template.children() );
  }      
} );

Ответ 2

В вашем представлении просто сделайте что-то вроде этого

var ItemView = Backbone.View.extend({
   tagName:  "div",   // I know it the default...

   render: function() {
     $(this.el).attr('id', 'id1').addClass('nice').html('Some Stuff'); 
   }       
});

Ответ 3

Вы можете установить свойства className и id в корневом элементе: http://documentcloud.github.com/backbone/#View-extend

var ItemView = Backbone.View.extend({
   tagName:  "div",   // I know it the default...
   className : 'nice',
   id : 'id1',
   render: function() {
     $(this.el).html("Some stuff");
   }       
});

РЕДАКТИРОВАТЬ Введен пример установки идентификатора на основе параметров конструктора

Если представления построены так, как указано:

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;
}
// ...

Ответ 4

Я знаю, это старый вопрос, но добавлен для справки. Кажется, это проще в новых базовых версиях. В Backbone 1.1 свойства id и className оцениваются в функции ensureElement (см. из источника) с использованием подчеркивания _.result, если className или id - это функция, она будет вызываться, в противном случае ее значение будет использоваться.

Итак, вы можете дать 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'); }
});

Ответ 5

Другие примеры не показывают, как фактически захватывать данные из модели. Чтобы динамически добавлять id и класс из данных модели:

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

Ответ 6

Вам нужно удалить tagName и объявить el.

'tagName' означает, что вы хотите создать основу для создания элемента. Если элемент уже существует в DOM, вы можете указать el как:

el: $('#emotions'),

и позже:

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

Ответ 7

Попробуйте назначить значения в методе инициализации, это будет автоматически присваивать id и классу атрибуту 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" ) ); 
    }
} );

Ответ 8

Здесь минимальный способ динамического изменения класса элемента вида с помощью модели и обновления его при изменении модели.

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