Динамическая установка id и className в представлениях Backbone.js

85

Я изучаю и использую 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({       
});

Как мне реализовать представление, чтобы 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!

Надав
источник

Ответы:

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

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

  • Начиная с Backbone 0.9.9:

    При объявлении View ... 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 представления служит бессмысленным элементом оболочки, внутри которого нужно вручную писать «семантический» код.

Нет никакой причины 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`'s top level element and
    // replacing the content of `this.el` with that.
    this.$el.empty().append( rendered_template.children() );
  }      
} );
JMM
источник
Отличный способ перезаписать свойство attribute функцией и снова вернуть объект!
Кел
2
@Kel, да, это хороший способ выполнить что-то динамическое, например, то, что задает вопрос, заполнение атрибутов данными модели без необходимости использовать повторяющийся код, в котором создаются экземпляры представлений. Вы, вероятно, знаете это, но на всякий случай, если это не очевидно, это особенность Backbone, в которой вы можете использовать функцию, которая возвращает хэш в качестве значения attributes, как и ряд других свойств Backbone, которые могут быть предоставлены как функция или некоторые другие тип значения. В этих случаях Backbone проверяет, является ли значение функцией, вызывает ее и использует возвращаемое значение.
JMM
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'); 
   }       
});
Дэн Брук
источник
Это правильный ответ на этот вопрос, и его следует принять
Ричард 4thelasers
13
Этот ответ не демонстрирует динамическую установку атрибутов представления на основе данных модели, он просто показывает альтернативный метод жесткого кодирования значений атрибутов.
JMM
3
@JMM - его примерный код также не использует данные модели. Этот ответ работает на основе его примера кода. Очевидно, что данные модели могут быть заменены на значения.
Клинт
5
@ Клинт, я бы не стал рассчитывать, что это будет очевидно для ОП. «Его примерный код также не использует данные модели». - это потому, что он не знает, как это сделать, и поэтому он попросил о помощи. Мне кажется очевидным, что он спрашивает, как установить attrs of view.el, используя данные модели, и не знает, как это сделать. Ответ даже не показывает, как это сделать, и зачем вам ждать рендеринга, чтобы сделать это в любом случае, или делать это каждый раз при рендеринге? «Этот ответ работает ...» - как это работает? Каждое созданное таким образом представление будет иметь одинаковые атрибуты. Единственное, что он показывает, - это как избежать обертки.
JMM
OP исчез с февраля '12. :( Вот еще +1 за этот ответ.
Almo
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");
   }       
});

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

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

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 03
Конечно, вы можете установить ID как хотите. Используйте функцию, переменную или что-нибудь еще. Мой код просто включает пример, показывающий, как установить значение в корневом элементе.
Jørgen
Я добавил пример, поясняющий, как устанавливать значения динамически на основе параметров конструктора.
Jørgen
Это правильный ответ. Он правильно использует функции Backbone для решения проблемы.
Marc-Antoine Lemieux
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'); })
Кобби
4

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

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"?
Гейб Рэйнбоу,
2

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

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

el: $('#emotions'),

и позже:

render: function() { 
     $(this.el).append(this.model.toJSON());
}
Jskulski
источник
2

Попробуйте назначить значения в методе инициализации, это будет напрямую динамически назначать идентификатор и класс атрибуту 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" ) ); 
    }
} );
Hemanth
источник
@Michel Pleasae ознакомился с этой документацией, 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 });
        }
    }
});
Эмиль Бержерон
источник