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框架提供了一套通用組件,可以在一些常見情況下重復使用,例如下拉菜單、復選框或日期選擇器。本頁面介紹如何使用這些通用組件。
技術名稱 |
簡短描述 |
---|---|
一個滑動組件,用于在觸摸滑動時執行操作 |
|
一個簡單的復選框組件,旁邊帶有標簽 |
|
可供選擇的顏色列表 |
|
全功能下拉菜單 |
|
一個使用選項卡導航頁面的組件 |
|
一個用于處理分頁的小組件 |
動作輪播?
位置?
@web/core/action_swiper/action_swiper
描述?
這是一個組件,可以在元素水平滑動時執行操作。Swiper將目標元素包裝起來,以添加操作。一旦用戶釋放swiper通過其寬度的一部分,操作就會執行。
<ActionSwiper onLeftSwipe="Object" onRightSwipe="Object">
<SomeElement/>
</ActionSwiper>
使用該組件的最簡單方法是在xml模板中直接將其用于目標元素,如上所示。但有時,您可能想要擴展現有元素,而不想復制模板。這也是可能的。
如果您想擴展現有元素的行為,您必須直接將該元素包裝在內部。此外,您可以有條件地添加屬性來管理元素何時可以進行滑動、其動畫以及執行操作所需的最小滑動部分。
您可以使用此組件輕松地與記錄、消息、列表中的項目等進行交互。

下面的示例創建了一個基本的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)語言時,操作會被排列。
屬性?
名稱 |
類型 |
描述 |
---|---|---|
|
|
可選布爾值,用于確定在滑動過程中是否存在翻譯效果 |
|
|
滑動結束后使用的可選動畫 ( |
|
|
如果存在,則可以向左滑動 actionswiper |
|
|
如果存在,則可以向右滑動 actionswiper |
|
|
可選的最小寬度比率,必須滑動才能執行操作 |
您可以同時使用 onLeftSwipe
和 onRightSwipe
屬性。
左/右滑所使用的“對象”必須包含:
action
,是一個可調用的Function
,用作回調函數。一旦在給定方向上完成了滑動,該操作將被執行。
icon
是要使用的圖標類,通常用于表示操作。它必須是一個string
。
bgColor
是背景顏色,用于裝飾操作??梢允且韵?bootstrap 上下文顏色 之一 (danger
,info
,secondary
,success
或warning
)。這些值必須提供以定義 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>
屬性?
名稱 |
類型 |
描述 |
---|---|---|
|
|
如果為真,則復選框被選中,否則未選中 |
|
|
如果為真,則復選框被禁用,否則它是啟用的 |
顏色列表?
位置?
@web/core/colorlist/colorlist
描述?
ColorList 允許您從預定義列表中選擇顏色。默認情況下,該組件顯示當前選定的顏色,并且在 canToggle
屬性存在之前不可擴展。不同的屬性可以改變其行為,始終展開列表,或使其在單擊后充當切換器,以顯示可用顏色的列表,直到選擇為止。
屬性?
名稱 |
類型 |
描述 |
---|---|---|
|
|
可選。顏色列表是否可以在單擊時展開列表 |
|
|
在組件中顯示的顏色列表。每種顏色都有一個唯一的 |
|
|
可選。如果為真,則列表始終展開 |
|
|
可選。如果為 true,則默認展開列表 |
|
|
選擇顏色后執行的回調函數 |
|
|
可選。所選顏色的 |
顏色 id
如下:
標識符 |
顏色 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
下拉菜單?
位置?
@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中的存在與否。
下拉菜單 |
類型 |
描述 |
---|---|---|
|
布爾值 |
初始下拉菜單打開狀態(默認為 |
|
字符串 |
應用于下拉菜單 |
|
字符串 |
應用于下拉菜單切換器 |
|
字符串 |
通過鍵盤切換打開的熱鍵 |
|
字符串 |
在切換器上添加工具提示 |
|
函數 |
在打開之前執行邏輯的鉤子??赡苁钱惒降?。 |
|
布爾值 |
如果為 true,則僅在單擊按鈕時切換下拉菜單(默認為 |
|
字符串 |
|
|
字符串 |
定義所需的菜單打開位置。自動應用RTL方向。應為有效的 usePosition 鉤子位置。(默認值: |
|
|
當設置為 |
一個 <DropdownItem/>
只是一個 span 元素 ( <span class="dropdown-item"/>
)。當選擇一個 <DropdownItem/>
時,它會調用它的 onSelected
屬性。如果這個屬性是一個方法,確保它被綁定,如果這個方法需要使用 this
值。
下拉菜單項 |
類型 |
描述 |
---|---|---|
|
函數 |
當下拉菜單項被選中時將調用的函數。 |
|
|
當選擇該項時,控制將關閉哪個父級下拉菜單: 無、最近的或所有(默認為 |
|
字符串 |
可選熱鍵以選擇該項 |
|
字符串 |
如果提供,則 DropdownItem 將變成 |
|
字符串 |
可選的標題屬性,將傳遞給DropdownItem的根節點。(默認值:未提供) |
|
對象 |
可選對象,包含應添加到根元素數據集的值。這可以使元素在編程中更容易找到,例如在測試或導覽中。 |
技術說明?
渲染后的 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"/>
中呈現。雖然不是必需的,但通常至少有一個DropdownItem
在menu
插槽中。
當多個下拉菜單共享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
。
屬性?
名稱 |
類型 |
描述 |
---|---|---|
|
|
可選。允許在不可見選項卡內部的元素之間進行錨點導航。 |
|
|
可選。設置在組件根部的類名。 |
|
|
可選。默認顯示的頁面 |
|
|
可選。選項卡方向是 |
|
|
可選項。頁面更改后執行的回調函數。 |
|
|
可選。包含從模板填充的 |
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" />
屬性?
名稱 |
類型 |
描述 |
---|---|---|
|
|
頁面第一個元素的索引。它從0開始,但分頁器顯示的是 |
|
|
頁面大小。 |
|
|
頁面可以達到的元素總數。 |
|
|
當分頁器修改頁面時調用的函數。此函數可以是異步的,當此函數執行時,分頁器不能被編輯。 |
|
|
允許單擊當前頁面進行編輯(默認為 |
|
|
默認情況下,將訪問鍵 |