第一章:組件?
本章介紹了 Owl 框架,這是一個專為 Odoo 設計的定制化組件系統。OWL 的主要構建塊是 組件 和 模板。
在Owl中,用戶界面的每個部分都由組件管理:它們保存邏輯并定義用于呈現用戶界面的模板。實際上,組件由一個小的JavaScript類表示,該類是 Component
類的子類。
Example
Counter
類實現了一個組件,它保存計數器的內部狀態并定義了如何遞增計數器。
const { Component, useState } = owl;
class Counter extends Component {
static template = "my_module.Counter";
state = useState({ value: 0 });
increment() {
this.state.value++;
}
}
Counter
類指定要呈現的模板名稱。該模板以 XML 編寫,定義了用戶界面的一部分。
<templates xml:space="preserve">
<t t-name="my_module.Counter" owl="1">
<p>Counter: <t t-esc="state.value"/></p>
<button class="btn btn-primary" t-on-click="increment">Increment</button>
</t>
</templates>
你可能已經注意到了 owl="1"
這個臨時屬性,它允許Odoo將Owl模板與舊的JavaScript框架模板區分開來。
讓我們花些時間熟悉Owl本身。下面,您將找到一系列練習,旨在快速了解和練習Owl的基礎知識。
目標
這一章我們將要達成的目標概述如下。

本章節中每個練習的解決方案都托管在 官方Odoo教程存儲庫 中。
1. 顯示計數器?
作為第一個練習,讓我們在位于 owl_playground/static/src/
的 Playground
組件中實現一個計數器。要查看結果,您可以使用瀏覽器訪問 /owl_playground/playground
路由。
小技巧
瀏覽器下載的Odoo JavaScript文件是經過壓縮的。為了調試方便,最好不要壓縮文件。切換到 帶資源的調試模式 ,這樣文件就不會被壓縮。
Exercise
修改
playground.js
,使其像 上面的例子 一樣作為計數器。您需要使用 useState 函數,以便在此組件讀取狀態對象的任何部分被修改時重新渲染組件。在同一組件中創建一個
increment
方法。修改
playground.xml
中的模板,以便顯示您的計數器變量。使用 t-esc 輸出數據。在模板中添加一個按鈕,并在按鈕中指定一個
t-on-click
屬性 <https://github.com/odoo/owl/blob/master/doc/reference/event_handling.md#event-handling> `_,以便在單擊按鈕時觸發 ` increment`方法。

2. 在組件中提取計數器?
目前我們在 Playground
組件中有一個計數器的邏輯,讓我們看看如何從中創建一個 `子組件<https://github.com/odoo/owl/blob/master/doc/reference/component.md#sub-components>`_。
Exercise
從
Playground
組件中提取計數器代碼到一個新的Counter
組件中。你可以先在同一個文件中完成,但完成后,請更新您的代碼,將
Counter
移動到它自己的文件中。請確保模板在自己的文件中,并且文件名相同。
重要
不要忘記在你的JavaScript文件中添加 / ** @odoo-module ** /
。更多信息請參考 這里 。
3. 待辦事項組件?
我們將在 owl_playground/static/src/
中創建新的組件,用于跟蹤待辦事項列表。這將在多個練習中逐步完成,介紹各種概念。
Exercise
創建一個
Todo
組件,它在 props 中接收一個todo
對象,并將其顯示出來。它應該顯示類似于 3. 買牛奶 。如果任務已完成,請在任務上添加Bootstrap類
text-muted
和text-decoration-line-through
。為此,您可以使用 `動態屬性<https://github.com/odoo/owl/blob/master/doc/reference/templates.md#dynamic-attributes>`_。修改
owl_playground/static/src/playground.js
和owl_playground/static/src/playground.xml
,使用一些硬編碼的屬性來顯示您的新Todo
組件,以便先進行測試。Example
setup() { ... this.todo = { id: 3, description: "buy milk", done: false }; }

另請參閱
4. 屬性驗證?
Todo
組件有一個隱式的API。它期望在其props中接收一個特定格式的todo對象的描述: id
, description
和 done
。讓我們使該API更加明確。我們可以添加一個props定義,讓Owl在 dev mode
中執行驗證步驟。您可以在 App配置
中激活 dev mode
。
對于每個組件進行屬性驗證是一個好的實踐。
Exercise
為
Todo
組件添加 props 驗證。確保在
owl_playground
中默認激活的開發模式下通過??梢酝ㄟ^修改owl_playground/static/src/main.js
中mount
函數的config
參數中的dev
屬性來激活或停用開發模式。從 props 中刪除
done
并重新加載頁面。驗證應該失敗。
5. 待辦事項清單?
現在,讓我們顯示一個待辦事項列表,而不僅僅是一個待辦事項?,F在,我們仍然可以硬編碼列表。
Exercise
將代碼更改為顯示待辦事項列表而不僅僅是一個,并在模板中使用
t-foreach
<https://github.com/odoo/owl/blob/master/doc/reference/templates.md#loops>`_。考慮如何使用
t-key
指令進行鍵控。

6. 添加一個待辦事項?
到目前為止,我們列表中的待辦事項是硬編碼的。讓我們通過允許用戶向列表中添加待辦事項使其更加有用。
Exercise
在任務列表上方添加一個輸入框,占位符為“輸入新任務”(Enter a new task)。
在
keyup
事件上添加名為addTodo
的event handler
<https://github.com/odoo/owl/blob/master/doc/reference/event_handling.md>`_。實現
addTodo
函數,檢查是否按下了回車鍵 (ev.keyCode === 13
),如果是,則使用輸入框當前的內容作為描述創建一個新的待辦事項。請確保它有一個唯一的ID。它可以只是一個在每個待辦事項中遞增的計數器。
然后,清除輸入框中的所有內容。
獎勵分:如果輸入為空,則不執行任何操作。
注解
注意到UI中沒有任何更新:這是因為Owl不知道它應該更新UI??梢酝ㄟ^在待辦事項列表中包裝 useState
鉤子來解決這個問題。
this.todos = useState([]);

另請參閱
7. 聚焦輸入框?
讓我們看看如何使用 t-ref 和 useRef 來訪問 DOM。
Exercise
當儀表板
mounted
時,將焦點放在上一個練習中的input
上獎勵分:將代碼提取到專門的 hook
useAutofocus
中。
另請參閱
8. 切換待辦事項?
現在,讓我們添加一個新功能:將待辦事項標記為已完成。這實際上比人們想象的要棘手。狀態的所有者與顯示它的組件不同。因此, Todo
組件需要向其父組件通信,表示待辦事項狀態需要切換。一種經典的方法是使用 回調屬性
<https://github.com/odoo/owl/blob/master/doc/reference/props.md#binding-function-props> _
toggleState`。
Exercise
在任務的id之前添加一個帶有屬性
type="checkbox"
的輸入框,如果狀態為done
,則必須選中。添加一個回調屬性
toggleState
。在
Todo
組件的輸入框上添加一個click
事件處理程序,并確保它使用 todo id 調用toggleState
函數。讓它工作起來!

9. 刪除待辦事項?
最后一步是讓用戶刪除待辦事項。
Exercise
添加一個新的回調屬性
removeTodo
。
小技巧
如果你正在使用數組來存儲待辦事項清單,你可以使用 JavaScript 的 splice
函數來從中刪除一個待辦事項。
// find the index of the element to delete
const index = list.findIndex((elem) => elem.id === elemId);
if (index >= 0) {
// remove the element at index from list
list.splice(index, 1);
}
在
Todo
組件的模板中插入<span class="fa fa-remove">
。每當用戶點擊它時,它應該調用
removeTodo
方法。

10. 具有插槽的通用組件?
Owl擁有一個強大的 `slot <https://github.com/odoo/owl/blob/master/doc/reference/slots.md>`_系統,允許您編寫通用組件。這對于在界面的不同部分之間因式分解常見布局非常有用。
Exercise
使用以下Bootstrap HTML結構編寫
Card
組件:<div class="card" style="width: 18rem;"> <img src="..." class="card-img-top" alt="..." /> <div class="card-body"> <h5 class="card-title">Card title</h5> <p class="card-text"> Some quick example text to build on the card title and make up the bulk of the card's content. </p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div>
這個組件應該有兩個插槽:一個用于標題,一個用于內容(默認插槽)。
Example
以下是如何使用它的方法:
<Card> <t t-set-slot="title">Card title</t> <p class="card-text">Some quick example text...</p> <a href="#" class="btn btn-primary">Go somewhere</a> </Card>
獎勵分:如果未提供
title
插槽,則根本不應該呈現h5
。
另請參閱
11. 更進一步?
Exercise
在
Card
組件上添加屬性驗證。嘗試在 props 驗證系統中表達需要一個
default
插槽和一個可選的title
插槽。