Spec-Zone.ru › Marionette.js 4

Отображение шаблонов представлений

В отличие от Backbone.View, представления Marionette предоставляют настраиваемое решение для рендеринга шаблона с данными и размещения результатов в DOM.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  tagName: 'h1',
  template: _.template('Contents')
});

const myView = new MyView();
myView.render();

В приведенном выше примере содержимое атрибута template будет отображено внутри тега <h1>, доступного по адресу myView.el.

Пример в реальном времени

Индекс документации

  • Что такое шаблон
  • Настройка шаблона представления
    • Использование представления без шаблона
  • Рендеринг шаблона
    • Использование пользовательского рендерера
    • Рендеринг в HTML
    • Рендеринг в DOM
  • Сериализация данных
    • Сериализация модели
    • Сериализация коллекции
    • Сериализация с CollectionView
  • Добавление контекстных данных
    • Что такое контекстные данные?

Что такое шаблон?

Шаблон — это функция, которая, получив данные, возвращает либо строку HTML, либо DOM. По умолчанию рендерер в Marionette ожидает, что шаблон вернет строку HTML. Зависимость Marionette — Underscore — поставляется со компилятором шаблонов для строк HTML.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template('<h1>Hello, world</h1>')
});

Это не обязательно должен быть шаблон underscore, вы можете передать свою собственную функцию рендеринга:

import Handlebars from 'handlebars';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: Handlebars.compile('<h1>Hello, {{ name }}')
});

Пример в реальном времени

Настройка шаблона представления

Представления Marionette используют метод getTemplate для определения шаблона, который будет использоваться для рендеринга в el. По умолчанию getTemplate предварительно определен в представлении как:

getTemplate() {
  return this.template
}

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

import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template('Hello World!'),
  getTemplate() {
    if (this.model.has('user')) {
      return _.template('Hello User!');
    }

    return this.template;
  }
});

Пример в реальном времени

Использование представления без шаблона

По умолчанию CollectionView не имеет определенного template и будет пытаться рендерить template только в том случае, если он определен. Для View могут быть ситуации, когда вы не хотите использовать шаблон.

Возможно, вам нужен только el или вы используете предобработанное содержимое.

В этом случае установка template в false предотвратит рендеринг шаблона. В случае с View это также предотвратит render события.

import { View } from 'backbone.marionette';

const MyIconButtonView = View.extend({
  template: false,
  tagName: 'button',
  className: '.icon-button',
  triggers: {
    'click': 'click'
  },
  onRender() {
    console.log('You will never see me!');
  }
});

Рендеринг шаблона

Каждый класс представления имеет рендерер, который по умолчанию передает данные представления данные представления в функцию шаблона и возвращает сгенерированную строку html.

Текущий рендерер по умолчанию по существу выглядит следующим образом:

import { View, CollectionView } from 'backbone.marionette';

function renderer(template, data) {
  return template(data);
}

View.setRenderer(renderer);
CollectionView.setRenderer(renderer);

До версии Marionette v4, рендерер по умолчанию был TemplateCache. Этот рендерер был выделен в отдельную библиотеку: https://github.com/marionettejs/marionette.templatecache и может быть использован с v4.

Использование пользовательского рендерера

Вы можете установить рендерер для класса представления, используя метод класса setRenderer. Рендерер принимает два аргумента. Первый — это шаблон, переданный представлению, а второй — данные, которые необходимо отобразить в шаблоне.

Вот пример, который позволяет template представления быть строкой шаблона underscore.

import _ from 'underscore';
import { View } from 'backbone.marionette';

View.setRenderer(function(template, data) {
  return _.template(template)(data);
});

const myView = new View({
  template: 'Hello <%- name %>!',
  model: new Backbone.Model({ name: 'World' })
});

myView.render();

// myView.el is <div>Hello World!</div>

Рендерер также можно настроить отдельно в любом расширенном представлении.

const MyHBSView = View.extend();

// Similar example as above but for handlebars
MyHBSView.setRenderer(function(template, data) {
  return Handlebars.compile(template)(data);
});

const myHBSView = new MyHBSView({
  template: 'Hello {{ name }}!',
  model: new Backbone.Model({ name: 'World' })
});

myHBSView.render();

// myView.el is <div>Hello World!</div>

Примечание. Эти примеры, хотя и функциональны, могут быть не оптимальными. По возможности рекомендуется предварительно скомпилировать шаблоны, что можно сделать для ряда шаблонизаторов с помощью различных плагинов для инструментов сборки, таких как Browserify или Webpack.

Рендеринг в HTML

По умолчанию рендеры Marionette возвращают HTML как строку. Эта строка передается методу attachElContents представления, который в свою очередь использует API DOM setContents для установки содержимого el представления с помощью DOM из строки.

Настройка attachElContents

Вы можете изменить способ привязки скомпилированного шаблона к el конкретного представления, переопределив attachElContents. Этот метод получает только результаты рендеринга представления и вызывается только в том случае, если рендерер возвращает значение.

Например, может потребоваться пропустить API DOM и установить html непосредственно:

attachElContent(html) {
  this.el.innerHTML = html;
}

Рендеринг в DOM

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

В следующем примере метод template, переданный рендереру, вернет элемент DOM, а затем, если представление уже отображено, использует morphdom для обновления DOM, или в противном случае установит el представления в результат шаблона. (В этом случае el представления, созданного при инициализации, будет перезаписан).

import morphdom from 'morphdom';
import { View } from 'backbone.marionette';

const VDomView = View.extend();

VDomView.setRenderer(function(template, data) {
  const el = template(data);

  if (this.isRendered()) {
    // Patch the view's el contents in the DOM
    morphdom(this.el, el, { childrenOnly: true });
    return;
  }

  this.setElement(el.cloneNode(true));
});

В этом случае, поскольку рендерер изменяет el напрямую, нет необходимости возвращать результат рендеринга шаблона для обработки представлением в attachElContents. Конечно, можно возвращать скомпилированный DOM и изменять attachElContents для обработки объекта DOM вместо строковой константы, но во многих случаях это может быть излишне сложно.

Существует множество возможностей для рендеринга с Marionette. Если вы ищете альтернативы по умолчанию, этот ресурс может быть полезным: https://github.com/blikblum/marionette.renderers#renderers

Сериализация данных

Marionette автоматически сериализует данные из model или collection для использования шаблоном во время рендеринга. Вы можете переопределить эту логику и предоставить сериализацию других данных с помощью метода serializeData. Метод вызывается без аргументов, но имеет контекст представления и должен возвращать объект JavaScript для использования шаблоном. Если serializeData не возвращает данные, шаблон все равно может получить дополнительный контекст или пустой объект для рендеринга.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template(`
    <div><% user.name %></div>
    <ul>
    <% _.each(groups, function(group) { %>
      <li><%- group.name %></li>
    <% }) %>
    </ul>
  `),
  serializeData() {
    // For this view I need both the
    // model and collection serialized
    return {
      user: this.serializeModel(),
      groups: this.serializeCollection(),
    };
  }
});

Примечание. Не следует использовать этот метод для добавления произвольных дополнительных данных в шаблон. Вместо этого используйте templateContext для добавления контекстных данных в шаблон.

Сериализация модели

Если представление имеет model, оно передаст атрибуты этой модели в шаблон.

import _ from 'underscore';
import Backbone from 'backbone';
import { View } from 'backbone.marionette';

const MyModel = Backbone.Model.extend({
  defaults: {
    name: 'world'
  }
});

const MyView = View.extend({
  template: _.template('<h1>Hello, <%- name %></h1>')
});

const myView = new MyView({ model: new MyModel() });

Пример в реальном времени

Способы сериализации model также могут быть настраиваемыми в зависимости от представления.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  serializeModel() {
    const data = _.clone(this.model.attributes);

    // serialize nested model data
    data.sub_model = data.sub_model.attributes;

    return data;
  }
});

Сериализация коллекции

Если представление не имеет model, но имеет collection, модели коллекции будут сериализованы в массив, предоставленный как атрибут items шаблону.

import _ from 'underscore';
import Backbone from 'backbone';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template(`
    <ul>
    <% _.each(items, function(item) { %>
      <li><%- item.name %></li>
    <% }) %>
    </ul>
  `)
});

const collection = new Backbone.Collection([
  {name: 'Steve'}, {name: 'Helen'}
]);

const myView = new MyView({ collection });

Пример в реальном времени

Способы сериализации collection также могут быть настраиваемыми в зависимости от представления.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  serializeCollection() {
    return _.map(this.collection.models, model => {
      const data = _.clone(model.attributes);

      // serialize nested model data
      data.sub_model = data.sub_model.attributes;

      return data;
    });
  }
});

Сериализация с CollectionView

Если вы используете template с CollectionView, который также не получает model, ваше CollectionView сериализует коллекцию для шаблона. Это может быть затратно и не нужно. Если ваше CollectionView имеет template, рекомендуется либо использовать пустое model, либо переопределить метод serializeData.

Добавление контекстных данных

Представления Marionette предоставляют атрибут templateContext, который используется для добавления дополнительной информации в ваши шаблоны. Это может быть объект или функция, возвращающая объект. Ключи возвращенного объекта будут добавлены к ключам модели или коллекции и будут доступны шаблону.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template('<h1>Hello, <%- name %></h1>'),
  templateContext: {
    name: 'World'
  }
});

Кроме того, контекстные данные переопределяют сериализованные данные.

import _ from 'underscore';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: _.template('<h1>Hello, <%- name %></h1>'),
  templateContext() {
    return {
      name: this.model.get('name').toUpperCase()
    };
  }
});

Вы также можете определить значение контекста шаблона как метод. Способ вызова этого метода определяется вашей системой шаблонизации. Например, с handlebars метод вызывается с контекстом данных, переданных шаблону.

import Handlebars from 'handlebars';
import Backbone from 'backbone';
import { View } from 'backbone.marionette';

const MyView = View.extend({
  template: Handlebars.compile(`
    <h1{{#if isDr}} class="dr"{{/if}}>Hello {{ fullName }}</h1>,
  `),
  templateContext: {
    isDr() {
      return (this.degree) === 'phd';
    },
    fullName() {
      // Because of Handlebars `this` here is the data object
      // passed to the template which is the result of the
      // templateContext mixed with the serialized data of the view
      return this.isDr() ? `Dr. { this.name }` : this.name;
    }
  }
});

const myView = new MyView({
  model: new Backbone.Model({ degree: 'masters', name: 'Joe' });
});

Примечание. Объект данных, передаваемый шаблону, не клонируется глубоко и в некоторых случаях вообще не клонируется. Будьте внимательны при изменении данных, передаваемых шаблону, чтобы косвенно не изменять данные вашей модели.

Что такое контекстные данные?

В то время как сериализация данных больше связана с получением данных представления в шаблон, контекст шаблона добавляет другие необходимые данные или, в некоторых случаях, выполняет дополнительные вычисления, которые выходят за рамки простого «сериализации» данных model или collection

import _ from 'underscore'
import { CollectionView } from 'backbone.marionette';
import GroupView from './group-view';

const MyCollectionView = CollectionView.extend({
  tagName: 'div',
  childViewContainer: 'ul',
  childView: GroupView,
  template: _.template(`
    <h1>Hello <% name %> of <% orgName %></h1>
    <div>You have <% stats.public %> group(s).</div>
    <div>You have <% stats.private %> group(s).</div>
    <h3>Groups:</h3>
    <ul></ul>
  `),
  templateContext() {
    const user = this.model;
    const organization = user.getOrganization();
    const groups = this.collection;

    return {
      orgName: organization.get('name'),
      name: user.getFullName(),
      stats: groups.countBy('type')
    };
  }
})

© 2017 Muted Solutions, LLC
Licensed under the MIT License.
https://marionettejs.com/docs/v4.0.0/view.rendering.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API