Подтвердить что ты не робот

Дополнительные обертки в макете и марионетке

Используя Backbone и Marionette, я создал новый макет, который входит в основной контент div на моей странице. Макет выглядит следующим образом:

<div id='dash-sidebar'>
    <div id='dash-profile'></div>
    <div id='dash-nav'></div> 
</div>
<div id='dash-content'></div>

Проблема заключается в том, что при рендеринге макета Backbone автоматически обертывает его в div, прежде чем помещать его в основной контент div следующим образом:

<div id='main-content'>    
  <div>
    <div id='dash-sidebar'>
      <div id='dash-profile'></div>
       <div id='dash-nav'></div> 
    </div>
    <div id='dash-content'></div>
  </div>
</div>

Я знаю, что я могу изменить элемент с tagName, но можно ли вообще обойти шаблон полностью и просто вставить его прямо в основной контент div на странице?

4b9b3361

Ответ 1

Каждый вид Backbone View должен быть представлен одним элементом. В вашем первом блоке HTML есть два элемента, поэтому он не может быть представлен представлением без предварительной упаковки его в внешний div.

Не могли бы вы реорганизовать свой макет, чтобы включить область main-content? Тогда макет el будет соответствовать всему внешнему div.

Еще одна вещь, которую следует попробовать, - использовать метод Backbone.View setElement(), чтобы переопределить создание внешнего div и вручную ввести HTML-код что вы хотите для элемента в представлении. Что-то вроде:

onRender: function() {
    this.setElement( /* the HTML you want for your layout */ );
}

Я не уверен, как это работает, если вы передали HTML, который имел два родительских элемента, а не только один.

Ответ 2

EDIT3: предупреждение! Этот ответ может быть устаревшим. Я получил комментарий, что этот ответ больше не работает, и у меня не было времени для расследования (я лично не использую этот метод).

Мне нравится использовать Twitter/Bootstrap в качестве моей библиотеки пользовательских интерфейсов, и у меня возникли проблемы со стилем таблицы из-за обтекания тега по умолчанию (в частности, <div> между моими <tbody> и my <tr> s).

После некоторого рытья я нашел что-то в документах Marionette на Region около как View присоединяет el. Backbone создает el из различных атрибутов View и сохраняет встроенный элемент в актуальном состоянии, поэтому он могут быть представлены в любое время. Поэтому я понял, что если view.el является родителем, почему бы просто не использовать содержимое HTML? Marionette также предоставляет способ создать пользовательский Region

Мне удалось запустить все, создав пользовательский Region с переопределенной функцией open. Таким образом, я могу указать, какие области я хочу обернуть тегом, а те, которые у меня нет. В следующем примере фрагмента я создаю свой собственный не-wrapping Region (NowrapRegion) и использую его в моем Layout (MyLayout) для моего <tbody> (представления, которые я передаю в своей реальной программе create <tr> с).

var NowrapRegion = Marionette.Region.extend({
  open: function(view){
    this.$el.html(view.$el.html());
  }
});

var MyLayout = Marionette.Layout.extend({
  template: templates.mylayout,
  regions: {
    foo: '#foo',
    columns: '#columns', //thead
    rows: { selector: '#rows', regionType: NowrapRegion } //tbody
  }
});

BOOM! Обертывание, когда я этого хочу, и нет, когда я этого не делаю.

EDIT:. Это, по-видимому, влияет на events, применяемый на уровне *View. Я еще не изучил его, но предупреждаю, что они, похоже, не срабатывают.

Является ли это "правильным" способом сделать это или нет, я не уверен. Я бы приветствовал любую обратную связь от @derick-bailey, если он это увидит.

EDIT2: @chris-neale предложил следующее, я еще не подтвердил это, но кажется звуковым. Спасибо!

Вместо копирования html в представлении, используя глубокий клон на дочерние узлы будут поддерживать события и данные.

var NowrapRegion = Marionette.Region.extend({
  open: function(view){
    view.$el.children().clone(true).appendTo(this.$el);
  }
});

Ответ 3

UPDATE: Marionette.Layout является расширением представления Backbone, так что это нормальное поведение, см. Документацию по базовым документам:

Свойство "el" ссылается на объект DOM, созданный в браузере. Каждое представление Backbone.js имеет свойство "el", и если оно не определено, Backbone.js построит свой собственный, который является пустым элементом div.

Итак, мой предыдущий ответ не имел ничего общего с вашей проблемой, извините.

Update:

Нашел вопрос 546 в Backbone gitHub по этому вопросу (который был закрыт как wontfix), jashkenas отправил этот комментарий, чтобы объяснить, почему его непросто реализовать:

Большая часть преимуществ использования Backbone Views заключается в том, что они всегда имеют доступ к своим элементам - независимо от того, был ли предоставлен шаблон (многие представления имеют несколько шаблоны) - независимо от того, какой вид присутствует в DOM или нет.

Это позволяет вам создавать и добавлять представления в DOM, и убедитесь, что все ваши события связаны правильно (потому что они делегированы из view.el).

Если вы можете придумать хорошую стратегию, которая позволяет "полные" шаблоны при сохранении вышеуказанных характеристик... затем поговорим об этом, но он должен позволить корневому элементу существуют без необходимости отображать содержимое шаблона (что может зависит от данных, которые могут не появиться дольше), и это должно позволить чтобы легко иметь несколько шаблонов.

Ответ 4

Я хотел достичь почти то же самое. Я хотел бы иметь все разметки HTML в моих шаблонах и позволить Backbone делать все остальное. Поэтому я написал следующий фрагмент, который удаляет дополнительный "div".

Сначала вы устанавливаете tagName "detect", а затем после первого рендера вы обнаруживаете tagName первого элемента внутри вашего представления. Очевидно, что это работает только тогда, когда вы предоставляете свою собственную оболочку в своем шаблоне.

// Single table row inside tbody
tableRowView = Backbone.Marionette.ItemView.extend({
  tagName: 'detect',
  template: function(data) {
    return Handlebars.templates['tablerow/single'](data);
  },
  onRender: function() {
    if (this.tagName == 'detect')
      this.tagName = this.$el.children().first().prop('tagName').toLowerCase();
    this.setElement( this.$el.children( this.tagName ) );
    var $parent = this.$el.parent( this.tagName );
    if ($parent.length) {
      this.$el.detach();
      this.$el.insertAfter($parent);
      $parent.remove();
    }
  },
  modelEvents: {
    "change": "render"
  }
});

Шаблон:

<tr>
  <td>{{artist}}</td>
  <td>{{title}}</td>
  <td>{{length}}</td>
</tr>

Однако я немного поиграл с ним - если кто-нибудь увидит какие-либо проблемы, которые может вызвать этот подход (производительность, память, зомби и т.д.), я был бы очень открыт, чтобы узнать о них.

Кстати, это, вероятно, можно было легко упаковать в плагин.