🦉 Движок QWeb 🦉

Содрежание

Введение

[QWeb] (https://www.odoo.com/documentation/13.0/reference/qweb.html) — это основной механизм шаблонов, используемый Odoo. Класс QWeb в проекте OWL является реализацией этой спецификации с несколькими интересными моментами:

  • компилирует шаблоны в функции, выводящие виртуальный DOM вместо строки. Это необходимо для компонентной системы.
  • у него есть несколько дополнительных директив: t-component, t-on, ...

В этом разделе мы представляем движок, а не язык шаблонов.

Описание элементов

Этот раздел посвящен коду javascript, который реализует спецификацию QWeb. Owl экспортирует класс QWeb в owl.QWeb. Чтобы использовать его, его нужн просто создать:

const qweb = new owl.QWeb();

Его API достаточно прост:

  • constructor(config): конструктор. Принимает необязательный объект конфигурации с необязательной строкой templates для добавления начальных шаблонов (дополнительную информацию о формате строки см. в addTemplates) и необязательную функцию перевода translateFn (см. раздел о переводах).

    const qweb = new owl.QWeb({ templates: TEMPLATES, translateFn: _t });
    
  • addTemplate(name, xmlStr, allowDuplicate): добавляет специфический шаблон.

    qweb.addTemplate("mytemplate", "<div>hello</div>");
    

    Если для необязательного параметра allowDuplicate установлено значение true, то QWeb просто проигнорирует шаблоны, добавленные во второй раз. В противном случае QWeb сломается.

  • addTemplates(xmlStr): добавить список шаблонов (определяется атрибутом t-name).

    const TEMPLATES = `
      <templates>
        <div t-name="App" class="main">main</div>
        <div t-name="OtherComponent">другой компонент</div>
      </templates>`;
    qweb.addTemplates(TEMPLATES);
    
  • render(name, context, extra): рендерит шаблон. Возвращает vnode, который является виртуальным представлением DOM (см. vdom doc).

    const vnode = qweb.render("App", component);
    
  • renderToString(name, context): рендерит шаблон, но возвращает строку html.

    const str = qweb.renderToString("someTemplate", somecontext);
    
  • registerTemplate(name, template): статическая функция для регистрации глобального шаблона QWeb. Это полезно для часто используемых компонентов в приложении и для того, чтобы сделать шаблон доступным для приложения без ссылки на фактический экземпляр QWeb.

    QWeb.registerTemplate("mytemplate", `<div>some template</div>`);
    
  • registerComponent(name, Component): статическая функция для регистрации компонента OWL в глобальном реестре QWeb. Глобально зарегистрированные компоненты могут использоваться в шаблонах (см. директиву t-component). Это полезно для часто используемых компонентов в приложении.

    class Dialog extends owl.Component { ... }
    QWeb.registerComponent("Dialog", Dialog);
    
    ...
    
    class ParentComponent extends owl.Component { ... }
    qweb.addTemplate("ParentComponent", "<div><Dialog/></div>");
    

В некотором смысле экземпляр QWeb является ядром приложения Owl. Это единственный обязательный элемент окружения. Таким образом, на него возлагается дополнительная ответственность: он может действовать как шина событий для внутренней вязи между классами Owl. По этой причине QWeb фактически расширяет EventBus.

Переводы

При правильной настройке движок Owl QWeb может переводить все отображаемые шаблоны. Для этого ему нужна функция перевода, которая принимает строку и возвращает строку.

Например:

const translations = {
  hello: "bonjour",
  yes: "oui",
  no: "non",
};
const translateFn = (str) => translations[str] || str;

const qweb = new QWeb({ translateFn });

После настройки все отрендеренные шаблоны будут переведены с помощью translateFn:

  • каждый текстовый узел будет заменен своим переводом,
  • каждое из следующих значений атрибутов также будет переведено: title, placeholder, label и alt,
  • перевод текстовых узлов можно отключить с помощью специального атрибута t-translation, если его значение равно off.

Итак, с помощью приведенного выше translateFn следующие шаблоны:

<div>hello</div>
<div t-translation="off">hello</div>
<div>Are you sure?</div>
<input placeholder="hello" other="yes"/>

будут отрендерены как:

<div>bonjour</div>
<div>hello</div>
<div>Are you sure?</div>
<input placeholder="bonjour" other="yes"/>

Обратите внимание, что перевод выполняется во время компиляции шаблона, а не во время его рендеринга.