🦉 Краткая информация 🦉

Компоненты Owl в приложении используются для формиования (динамически) дерева компонентов.

        Root
        /   \
       A     B
      / \
     C   D

State(Состояние): каждый компонент может управлять своим локальным состоянием. Это простой ES6 класс, без каких либо правил:

class Counter extends Component {
  static template = xml`
    <button t-on-click="increment">
      Click Me! [<t t-esc="state.value"/>]
    </button>`;

  state = { value: 0 };

  increment() {
    this.state.value++;
    this.render();
  }
}

Пример выше показывает компонет с локальным состоянием. Обратите внимане что нет ничего необычного в объекте state, и нам надо самостоятельно вызывать функцию render каждый раз, когда мы вносим изменения в наше состояние. Такой подход очень быстро начне бесить (и снижает эффектвность сли мы начинаем использовать его слишком часто). Для этого есть способ лучше: использовать хук useState, который превращает объект в реактивную версию самого себя:

const { useState } = owl.hooks;

class Counter extends Component {
  static template = xml`
    <button t-on-click="increment">
      Click Me! [<t t-esc="state.value"/>]
    </button>`;

  state = useState({ value: 0 });

  increment() {
    this.state.value++;
  }
}

Обратите внимание, что обработчик t-on-click можно даже заменить встроенным выражением:

<button t-on-click="state.value++">

Props(Свойства, пропсы): дочернему компоненту может часто требоваться получать информацию от его родителя. Это реализуется путем добавления необходимой информации в шаблон. This родителя будет доступен дочернему комненту в объекте props. Обратите внимание, что здесь есть важное правило: информация, содержащаяся в объекте props, не принадлежит дочернему компоненту и никогда не должна им изменяться.

class Child extends Component {
  static template = xml`<div>Hello <t t-esc="props.name"/></div>`;
}

class Parent extends Component {
  static template = xml`
    <div>
        <Child name="'Owl'" />
        <Child name="'Framework'" />
    </div>`;
  static components = { Child };
}

Communication(Коммуникация): существует несколько способов передачи информации между компонентами. Вот два наиболее важных:

  • от родительского компонента - дочернему: используя объект props,
  • от дочернего компонента - родительскому: используя запуск событий(эвентов),

Следующий пример показывает оба механизма:

class OrderLine extends Component {
  static template = xml`
    <div t-on-click="add">
        <div><t t-esc="props.line.name"/></div>
        <div>Quantity: <t t-esc="props.line.quantity"/></div>
    </div>`;

  add() {
    this.trigger("add-to-order", { line: this.props.line });
  }
}

class Parent extends Component {
  static template = xml`
    <div t-on-add-to-order="addToOrder">
        <OrderLine
            t-foreach="orders"
            t-as="line"
            line="line" />
    </div>`;
  static components = { OrderLine };
  orders = useState([
    { id: 1, name: "Coffee", quantity: 0 },
    { id: 2, name: "Tea", quantity: 0 },
  ]);

  addToOrder(event) {
    const line = event.detail.line;
    line.quantity++;
  }
}

В этом примере компонент OrderLine инициирует событиеadd-to-order. Что в свою очередь породит событие DOM, которое начнет "всплывать" по DOM дереву. Оно (событие) будет перехвачено родительским компонентом, который в свою очередь получит строку (по ключу detail) и затем увеличит ее количество. Вот детали того, ка работает обработка событий.

Обратите внимание, и в случае, если бы компонент OrderLine напрямую модифицировал объект line. Однако это не очень хорошая практика: это работает только потому, что объект props, полученный дочерним компонентом, является реактивным, поэтому дочерний компонент затем связан с реализацией родительского компонента.