Owl 組件?

Odoo JavaScript框架使用了一個名為Owl的自定義組件框架。它是一個聲明式組件系統,受Vue和React的啟發而設計。組件使用 QWeb模板 定義,并且使用一些Owl特定的指令進行增強。官方的 Owl文檔 包含了完整的參考和教程。

重要

盡管代碼可以在 web 模塊中找到,但它是從一個單獨的GitHub存儲庫中維護的。因此,對Owl的任何修改都應通過https://github.com/odoo/owl上的拉取請求進行。

注解

目前,所有的Odoo版本(從14版本開始)共享同一個Owl版本。

使用 Owl 組件?

`Owl文檔`_已經詳細記錄了Owl框架的內容,因此本頁僅提供Odoo特定的信息。但首先,讓我們看看如何在Odoo中創建一個簡單的組件。

const { useState } = owl.hooks;
const { xml } = owl.tags;

class MyComponent extends Component {
    setup() {
        this.state = useState({ value: 1 });
    }

    increment() {
        this.state.value++;
    }
}
MyComponent.template = xml
    `<div t-on-click="increment">
        <t t-esc="state.value">
    </div>`;

此示例顯示Owl作為全局命名空間中的庫可用,可以像Odoo中的大多數庫一樣簡單使用。請注意,我們在此將模板定義為靜態屬性,但沒有使用“static”關鍵字,因為某些瀏覽器不支持它(Odoo JavaScript代碼應符合Ecmascript 2019標準)。

我們在javascript代碼中使用 xml 助手定義模板。然而,這只是為了入門。實際上,Odoo中的模板應該在xml文件中定義,以便進行翻譯。在這種情況下,組件應該只定義模板名稱。

實際上,大多數組件應該定義2或3個文件,位于同一位置:一個JavaScript文件( my_component.js ),一個模板文件( my_component.xml ),以及可選的scss(或css)文件( my_component.scss )。然后,這些文件應該被添加到某個資產包中。Web框架將負責加載JavaScript / CSS文件,并將模板加載到Owl中。

以下是如何定義上述組件的方法:

const { useState } = owl.hooks;

class MyComponent extends Component {
    ...
}
MyComponent.template = 'myaddon.MyComponent';

現在模板位于相應的xml文件中:

<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="myaddon.MyComponent" owl="1">
  <div t-on-click="increment">
    <t t-esc="state.value"/>
  </div>
</t>

</templates>

Odoo代碼尚未完全轉換為Owl,因此需要一種方法來區分Owl模板(新代碼)和舊模板(用于組件)。為了以向后兼容的方式實現這一點,所有新模板都應該定義為 owl 屬性設置為1。

注解

不要忘記在您的Owl模板中設置 owl="1" !

注解

模板名稱應遵循 addon_name.ComponentName 的約定。

另請參閱

最佳實踐?

首先,組件是類,因此它們有一個構造函數。但是構造函數是javascript中不可重寫的特殊方法。由于這是Odoo中偶爾有用的模式,我們需要確保Odoo中沒有任何組件直接使用構造函數方法。相反,組件應該使用 setup 方法:

// correct:
class MyComponent extends Component {
    setup() {
        // initialize component here
    }
}

// incorrect. Do not do that!
class IncorrectComponent extends Component {
    constructor(parent, props) {
        // initialize component here
    }
}

另一個好的實踐是使用一致的模板命名約定: addon_name.ComponentName 。這可以防止Odoo插件之間的名稱沖突。

參考列表?

Odoo Web客戶端是使用 Owl 組件構建的。為了使它更容易,Odoo JavaScript框架提供了一套通用組件,可以在一些常見情況下重復使用,例如下拉菜單、復選框或日期選擇器。本頁面介紹如何使用這些通用組件。

技術名稱

簡短描述

ActionSwiper

一個滑動組件,用于在觸摸滑動時執行操作

復選框

一個簡單的復選框組件,旁邊帶有標簽

顏色列表

可供選擇的顏色列表

下拉菜單

全功能下拉菜單

筆記本

一個使用選項卡導航頁面的組件

分頁器

一個用于處理分頁的小組件

動作輪播?

位置?

@web/core/action_swiper/action_swiper

描述?

這是一個組件,可以在元素水平滑動時執行操作。Swiper將目標元素包裝起來,以添加操作。一旦用戶釋放swiper通過其寬度的一部分,操作就會執行。

<ActionSwiper onLeftSwipe="Object" onRightSwipe="Object">
  <SomeElement/>
</ActionSwiper>

使用該組件的最簡單方法是在xml模板中直接將其用于目標元素,如上所示。但有時,您可能想要擴展現有元素,而不想復制模板。這也是可能的。

如果您想擴展現有元素的行為,您必須直接將該元素包裝在內部。此外,您可以有條件地添加屬性來管理元素何時可以進行滑動、其動畫以及執行操作所需的最小滑動部分。

您可以使用此組件輕松地與記錄、消息、列表中的項目等進行交互。

ActionSwiper使用示例

下面的示例創建了一個基本的ActionSwiper組件。在這里,可以在兩個方向上進行滑動。

<ActionSwiper
  onRightSwipe="
    {
      action: '() => Delete item',
      icon: 'fa-delete',
      bgColor: 'bg-danger',
    }"
  onLeftSwipe="
    {
      action: '() => Star item',
      icon: 'fa-star',
      bgColor: 'bg-warning',
    }"
>
  <div>
    Swipable item
  </div>
</ActionSwiper>

注解

在使用從右到左(RTL)語言時,操作會被排列。

屬性?

名稱

類型

描述

animationOnMove

Boolean

可選布爾值,用于確定在滑動過程中是否存在翻譯效果

animationType

String

滑動結束后使用的可選動畫 ( bounceforwards )。

onLeftSwipe

Object

如果存在,則可以向左滑動 actionswiper

onRightSwipe

Object

如果存在,則可以向右滑動 actionswiper

swipeDistanceRatio

Number

可選的最小寬度比率,必須滑動才能執行操作

您可以同時使用 onLeftSwipeonRightSwipe 屬性。

左/右滑所使用的“對象”必須包含:

  • action ,是一個可調用的 Function ,用作回調函數。一旦在給定方向上完成了滑動,該操作將被執行。

  • icon 是要使用的圖標類,通常用于表示操作。它必須是一個 string 。

  • bgColor 是背景顏色,用于裝飾操作??梢允且韵?bootstrap 上下文顏色 之一 ( danger , info , secondary , successwarning )。

這些值必須提供以定義 swiper 的行為和視覺效果。

示例:擴展現有組件?

在下面的示例中,您可以使用 xpath 將現有元素包裝在 ActionSwiper 組件中。這里,已經添加了一個 swiper 來標記郵件中的消息為已讀。

<xpath expr="//*[hasclass('o_Message')]" position="after">
  <ActionSwiper
    onRightSwipe="messaging.device.isMobile and messageView.message.isNeedaction ?
      {
        action: () => messageView.message.markAsRead(),
        icon: 'fa-check-circle',
        bgColor: 'bg-success',
      } : undefined"
  />
</xpath>
<xpath expr="//ActionSwiper" position="inside">
  <xpath expr="//*[hasclass('o_Message')]" position="move"/>
</xpath>

復選框?

位置?

@web/core/checkbox/checkbox

描述?

這是一個簡單的復選框組件,旁邊有一個標簽。復選框與標簽相連:每當單擊標簽時,復選框就會切換。

<CheckBox value="boolean" disabled="boolean" t-on-change="onValueChange">
  Some Text
</CheckBox>

屬性?

名稱

類型

描述

value

boolean

如果為真,則復選框被選中,否則未選中

disabled

boolean

如果為真,則復選框被禁用,否則它是啟用的

顏色列表?

位置?

@web/core/colorlist/colorlist

描述?

ColorList 允許您從預定義列表中選擇顏色。默認情況下,該組件顯示當前選定的顏色,并且在 canToggle 屬性存在之前不可擴展。不同的屬性可以改變其行為,始終展開列表,或使其在單擊后充當切換器,以顯示可用顏色的列表,直到選擇為止。

屬性?

名稱

類型

描述

canToggle

boolean

可選。顏色列表是否可以在單擊時展開列表

colors

array

在組件中顯示的顏色列表。每種顏色都有一個唯一的 id 。

forceExpanded

boolean

可選。如果為真,則列表始終展開

isExpanded

boolean

可選。如果為 true,則默認展開列表

onColorSelected

function

選擇顏色后執行的回調函數

selectedColor

number

可選。所選顏色的 id

顏色 id 如下:

標識符

顏色

0

無顏色

1

Red

2

橙色

3

黃色

4

淺藍色

5

深紫色

6

鮭魚粉

7

中藍色

8

深藍色

9

紫紅色

12

綠色

11

紫色

位置?

@web/core/dropdown/dropdown@web/core/dropdown/dropdown_item

描述?

下拉菜單是非常復雜的組件。它們需要提供許多功能,例如:

  • 點擊時切換項目列表

  • 直接兄弟下拉菜單:當一個打開時,懸停時切換其他菜單

  • 點擊外部關閉

  • 當選擇一個項目時,可選擇關閉項目列表

  • 當項目被選中時調用一個函數

  • 支持多級子菜單下拉框

  • SIY:自己設計樣式

  • 可配置的熱鍵,用于打開/關閉下拉菜單或選擇下拉菜單項

  • 鍵盤導航(箭頭、Tab、Shift+Tab、Home、End、Enter 和 Escape)

  • 每當頁面滾動或調整大小時重新定位自身

  • 智能地選擇它應該打開的方向(從右到左的方向會自動處理)。

為了一勞永逸地解決這些問題,Odoo框架提供了一組兩個組件:一個 Dropdown 組件(實際的下拉菜單)和 DropdownItem ,用于列表中的每個元素。

<Dropdown>
  <t t-set-slot="toggler">
    <!-- "toggler" slot content is rendered inside a button -->
    Click me to toggle the dropdown menu !
  </t>
  <!-- "default" slot content is rendered inside a div -->
  <DropdownItem onSelected="selectItem1">Menu Item 1</DropdownItem>
  <DropdownItem onSelected="selectItem2">Menu Item 2</DropdownItem>
</Dropdown>

屬性?

一個 <Dropdown/> 組件就是一個簡單的 <div class="dropdown"/> ,旁邊有一個 <button class="dropdown-toggle"/> 和菜單div( <div class="dropdown-menu"/> )。按鈕負責菜單在DOM中的存在與否。

下拉菜單

類型

描述

startOpen

布爾值

初始下拉菜單打開狀態(默認為 false

menuClass

字符串

應用于下拉菜單 <div class="dropdown-menu"/> 的附加CSS類

togglerClass

字符串

應用于下拉菜單切換器 <button class="dropdown-toggle"/> 的額外 CSS 類

hotkey

字符串

通過鍵盤切換打開的熱鍵

tooltip

字符串

在切換器上添加工具提示

beforeOpen

函數

在打開之前執行邏輯的鉤子??赡苁钱惒降?。

manualOnly

布爾值

如果為 true,則僅在單擊按鈕時切換下拉菜單(默認為 false

title

字符串

<button class="dropdown-toggle"/> 的”title”屬性內容(默認值: 無)

position

字符串

定義所需的菜單打開位置。自動應用RTL方向。應為有效的 usePosition 鉤子位置。(默認值: bottom-start )

toggler

"parent"undefined

當設置為 "parent" 時,不會渲染 <button class="dropdown-toggle"/> (因此 toggler 插槽將被忽略),并且切換功能由父節點處理(例如使用情況:數據透視表單元格)。 (默認值: undefined

一個 <DropdownItem/> 只是一個 span 元素 ( <span class="dropdown-item"/> )。當選擇一個 <DropdownItem/> 時,它會調用它的 onSelected 屬性。如果這個屬性是一個方法,確保它被綁定,如果這個方法需要使用 this 值。

下拉菜單項

類型

描述

onSelected

函數

當下拉菜單項被選中時將調用的函數。

parentClosingMode

none | closest | all

當選擇該項時,控制將關閉哪個父級下拉菜單: 無、最近的或所有(默認為 所有

hotkey

字符串

可選熱鍵以選擇該項

href

字符串

如果提供,則 DropdownItem 將變成 <a href="value" class="dropdown-item"/> 而不是 <span class="dropdown-item"/> 。(默認值:未提供)

title

字符串

可選的標題屬性,將傳遞給DropdownItem的根節點。(默認值:未提供)

dataset

對象

可選對象,包含應添加到根元素數據集的值。這可以使元素在編程中更容易找到,例如在測試或導覽中。

技術說明?

渲染后的 DOM 結構如下:

<div class="dropdown">
    <button class="dropdown-toggle">Click me !</button>
    <!-- following <div/> will or won't appear in the DOM depending on the state controlled by the preceding button -->
    <div class="dropdown-menu">
        <span class="dropdown-item">Menu Item 1</span>
        <span class="dropdown-item">Menu Item 2</span>
    </div>
</div>

要正確使用 <Dropdown/> 組件,您需要填充兩個 `OWL插槽<https://github.com/odoo/owl/blob/master/doc/reference/slots.md>`_

  • toggler 插槽: 它包含您的下拉菜單的 toggler 元素,并在下拉菜單 button 內部呈現(除非將 toggler prop 設置為 parent ),

  • default 插槽: 它包含下拉菜單本身的 元素 ,并在 <div class="dropdown-menu"/> 中呈現。雖然不是必需的,但通常至少有一個 DropdownItemmenu 插槽中。

當多個下拉菜單共享DOM中的同一父元素時,它們被視為一組,并且將相互通知其狀態更改。這意味著當其中一個下拉菜單打開時,其他下拉菜單將在鼠標懸停時自動打開,無需點擊。

示例:直接兄弟下拉菜單?

當單擊一個下拉切換器( 文件 、 編輯關于 )時,其他下拉切換器將在懸停時自動打開。

<div>
  <Dropdown>
    <t t-set-slot="toggler">File</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-open')">Open</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-document')">New Document</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-spreadsheet')">New Spreadsheet</DropdownItem>
  </Dropdown>
  <Dropdown>
    <t t-set-slot="toggler">Edit</t>
    <DropdownItem onSelected="() => this.onItemSelected('edit-undo')">Undo</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('edit-redo')">Redo</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('edit-find')">Search</DropdownItem>
  </Dropdown>
  <Dropdown>
    <t t-set-slot="toggler">About</t>
    <DropdownItem onSelected="() => this.onItemSelected('about-help')">Help</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('about-update')">Check update</DropdownItem>
  </Dropdown>
</div>

示例:多級下拉菜單(使用 t-call?

這個例子展示了如何創建一個帶有子菜單的 文件 下拉菜單,包括 新建另存為... 子元素。

<t t-name="addon.Dropdown.File" owl="1">
  <Dropdown>
    <t t-set-slot="toggler">File</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-open')">Open</DropdownItem>
    <t t-call="addon.Dropdown.File.New"/>
    <DropdownItem onSelected="() => this.onItemSelected('file-save')">Save</DropdownItem>
    <t t-call="addon.Dropdown.File.Save.As"/>
  </Dropdown>
</t>

<t t-name="addon.Dropdown.File.New" owl="1">
  <Dropdown>
    <t t-set-slot="toggler">New</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-document')">Document</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-spreadsheet')">Spreadsheet</DropdownItem>
  </Dropdown>
</t>

<t t-name="addon.Dropdown.File.Save.As" owl="1">
  <Dropdown>
    <t t-set-slot="toggler">Save as...</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-save-as-csv')">CSV</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-save-as-pdf')">PDF</DropdownItem>
  </Dropdown>
</t>

示例:多級下拉菜單(嵌套)?

<Dropdown>
  <t t-set-slot="toggler">File</t>
  <DropdownItem onSelected="() => this.onItemSelected('file-open')">Open</DropdownItem>
  <Dropdown>
    <t t-set-slot="toggler">New</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-document')">Document</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-new-spreadsheet')">Spreadsheet</DropdownItem>
  </Dropdown>
  <DropdownItem onSelected="() => this.onItemSelected('file-save')">Save</DropdownItem>
  <Dropdown>
    <t t-set-slot="toggler">Save as...</t>
    <DropdownItem onSelected="() => this.onItemSelected('file-save-)as-csv'">CSV</DropdownItem>
    <DropdownItem onSelected="() => this.onItemSelected('file-save-)as-pdf'">PDF</DropdownItem>
  </Dropdown>
</Dropdown>

示例:遞歸多級下拉菜單?

在這個例子中,我們遞歸調用一個模板來顯示類似樹形結構的內容。

<t t-name="addon.MainTemplate" owl="1">
  <div>
    <t t-call="addon.RecursiveDropdown">
      <t t-set="name" t-value="'Main Menu'" />
      <t t-set="items" t-value="state.menuItems" />
    </t>
  </div>
</t>

<t t-name="addon.RecursiveDropdown" owl="1">
  <Dropdown>
    <t t-set-slot="toggler"><t t-esc="name"/></t>
      <t t-foreach="items" t-as="item" t-key="item.id">

        <!-- If this item has no child: make it a <DropdownItem/> -->
        <t t-if="!item.childrenTree.length">
          <DropdownItem onSelected="() => this.onItemSelected(item)" t-esc="item.name"/>
        </t>
        <!-- Else: recursively call the current dropdown template. -->
        <t t-else="" t-call="addon.RecursiveDropdown">
          <t t-set="name" t-value="item.name" />
          <t t-set="items" t-value="item.childrenTree" />
        </t>

      </t>
    </t>
  </Dropdown>
</t>

筆記本電腦?

位置?

@web/core/notebook/notebook

描述?

筆記本是用于在選項卡界面中顯示多個頁面的。選項卡可以位于元素頂部以水平方式顯示,也可以位于左側以垂直布局顯示。

定義 Notebook 頁面實例有兩種方法,一種是使用 slot ,另一種是通過傳遞專用的 props 。

屬性?

名稱

類型

描述

anchors

object

可選。允許在不可見選項卡內部的元素之間進行錨點導航。

className

string

可選。設置在組件根部的類名。

defaultPage

string

可選。默認顯示的頁面 id 。

orientation

string

可選。選項卡方向是 水平 還是 垂直 。

onPageUpdate

function

可選項。頁面更改后執行的回調函數。

pages

array

可選。包含從模板填充的 page 列表。

Example

第一種方法是將頁面設置在組件的插槽中。

<Notebook orientation="'vertical'">
  <t t-set-slot="page_1" title="'Page 1'" isVisible="true">
    <h1>My First Page</h1>
    <p>It's time to build Owl components. Did you read the documentation?</p>
  </t>
  <t t-set-slot="page_2" title="'2nd page'" isVisible="true">
    <p>Wise owl's silent flight. Through the moonlit forest deep, guides my path to code</p>
  </t>
</Notebook>

另一種定義頁面的方法是通過傳遞 props。如果某些頁面共享相同的結構,則此方法很有用。首先為您可能使用的每個頁面模板創建一個組件。

import { Notebook } from "@web/core/notebook/notebook";

class MyTemplateComponent extends owl.Component {
  static template = owl.tags.xml`
    <h1 t-esc="props.title" />
    <p t-esc="props.text" />
  `;
}

class MyComponent extends owl.Component {
  get pages() {
    return [
      {
        Component: MyTemplateComponent,
        title: "Page 1",
        props: {
          title: "My First Page",
          text: "This page is not visible",
        },
      },
      {
        Component: MyTemplateComponent,
        id: "page_2",
        title: "Page 2",
        props: {
          title: "My second page",
          text: "You're at the right place!",
        },
      },
    ]
  }
}
MyComponent.template = owl.tags.xml`
  <Notebook defaultPage="'page_2'" pages="pages" />
`;

這里展示了兩個例子:

具有垂直和水平布局的示例

分頁器?

位置?

@web/core/pager/pager

描述?

分頁器是一個小組件,用于處理分頁。一個頁面由一個 offset 和一個 limit (頁面大?。┒x。它顯示當前頁面和元素的 total 數量,例如 “9-12 / 20”。在前面的示例中, offset 是 8, limit 是 4, total 是 20。它有兩個按鈕(”上一頁”和”下一頁”),用于在頁面之間導航。

注解

分頁器可以在任何地方使用,但它的主要用途是在控制面板中。請參見 usePager 鉤子以操作控制面板的分頁器。

<Pager offset="0" limit="80" total="50" onUpdate="doSomething" />

屬性?

名稱

類型

描述

offset

number

頁面第一個元素的索引。它從0開始,但分頁器顯示的是 offset + 1 。

limit

number

頁面大小。 offsetlimit 的和對應于頁面上最后一個元素的索引。

total

number

頁面可以達到的元素總數。

onUpdate

function

當分頁器修改頁面時調用的函數。此函數可以是異步的,當此函數執行時,分頁器不能被編輯。

isEditable

boolean

允許單擊當前頁面進行編輯(默認為 true )。

withAccessKey

boolean

默認情況下,將訪問鍵 p 綁定到上一頁按鈕,將訪問鍵 n 綁定到下一頁按鈕。