第一章:Owl 组件¶
本章介绍了 Owl 框架 <https://github.com/odoo/owl>
_,一个专为 Odoo 定制的组件系统。OWL 的主要构建模块是 components <https://github.com/odoo/owl/blob/master/doc/reference/component.md>
_ 和 templates <https://github.com/odoo/owl/blob/master/doc/reference/templates.md>
_。
在Owl中,用户界面的每个部分都由组件管理:它们保存逻辑并定义用于呈现用户界面的模板。实际上,组件由一个小的JavaScript类表示,该类是 Component
类的子类。
在开始练习之前,请确保您已经按照本教程介绍中描述的所有步骤进行操作: 教程介绍 。
每个章节的练习解决方案都托管在 官方Odoo教程存储库。建议先尝试解决问题,不要看解决方案!
小技巧
如果您使用 Chrome 作为您的网络浏览器,您可以安装 Owl Devtools
扩展。这个扩展提供了许多功能,帮助您理解和分析任何 Owl 应用程序。
在本章中,我们使用 owl_playground
插件,它提供了一个简化的环境,只包含 Owl 和一些其他文件。目标是学习 Owl 本身,而不依赖于 Odoo web 客户端代码。要开始,请使用浏览器打开 /owl_playground/playground
路由:它应该显示一个带有文本 hello world 的 Owl 组件。
示例:一个 Counter
组件¶
首先,让我们来看一个简单的例子。下面显示的 Counter
组件是一个维护内部数字值、显示它并在用户点击按钮时更新它的组件。
import { Component, useState } from "@odoo/owl";
class Counter extends Component {
static template = "my_module.Counter";
setup() {
this.state = useState({ value: 0 });
}
increment() {
this.state.value++;
}
}
The Counter
component specifies the name of the template to render. The template is written in XML
and defines a part of user interface:
<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模板与QWeb模板不同:它们可以包含其他指令,例如 t-on-click
。
1. 显示计数器¶
作为第一个练习,让我们在 Playground
组件中实现一个计数器,该组件位于 owl_playground/static/src/
。要查看结果,您可以使用浏览器访问 /owl_playground/playground
路由。
Exercise
修改
playground.js
使其像上面的示例一样作为计数器。您需要使用 useState hook 以便在此组件读取的状态对象的任何部分被修改时重新渲染组件。在同一组件中创建一个
increment
方法。修改
playground.xml
中的模板,以便显示您的计数变量。使用 t-esc 输出数据。在模板中添加一个按钮,并在按钮中指定一个 t-on-click 属性,以便在按钮被点击时触发
increment
方法。
小技巧
浏览器下载的Odoo JavaScript文件是被压缩的。为了调试方便,最好不要压缩文件。切换到 带资源的调试模式 ,这样文件就不会被压缩。
2. 在组件中提取计数器¶
现在我们在 Playground
组件中有一个计数器的逻辑,让我们看看如何从中创建一个 sub-component
。
Exercise
从
Playground
组件中提取计数器代码到一个新的Counter
组件中。你可以先在同一个文件中完成,但是一旦完成后,请更新你的代码,将
Counter
移动到它自己的文件夹和文件中。从./counter/counter
相对导入它。确保模板在它自己的文件中,文件名相同。
重要
不要忘记在您的JavaScript文件中添加 /** @odoo-module **/
。更多信息请参考 这里。
3. 待办事项组件¶
我们将在 owl_playground/static/src/
中创建新的组件,以跟踪待办事项列表。这将通过多个练习逐步完成,介绍各种概念。
Exercise
创建一个
Todo
组件,它在 props 中接收一个todo
对象,并将其显示出来。它应该显示类似于 3. 买牛奶 的内容。如果任务已完成,可以在任务上添加Bootstrap类
text-muted
和text-decoration-line-through
。为了实现这一点,您可以使用dynamic attributes <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
组件,并使用一些硬编码的 props 进行测试。Example
setup() { ... this.todo = { id: 3, description: "buy milk", done: false }; }
4. 属性验证¶
组件 Todo
有一个隐式的 API。它期望在其 props 中接收一个特定格式的 todo 对象的描述:id
、description
和 done
。让我们将该 API 更加明确。我们可以添加一个 props 定义,让 Owl 在 dev mode
中执行验证步骤 <https://github.com/odoo/owl/blob/master/doc/reference/app.md#dev-mode>`_。您可以在 App 配置 中激活 dev mode。
对于每个组件进行属性验证是一个好的实践。
Exercise
在
Todo
组件中添加 props validation。打开浏览器的开发工具,切换到 Console 标签,并确保在开发模式下通过了属性验证,在
owl_playground
中默认激活了开发模式。开发模式可以通过修改owl_playground/static/src/main.js
中 mount 函数的config
参数中的dev
属性来激活和停用。从 props 中删除
done
并重新加载页面。验证应该失败。
5. 待办事项清单¶
现在,让我们显示一个待办事项列表,而不仅仅是一个待办事项。现在,我们仍然可以硬编码列表。
Exercise
将代码更改为显示一个待办事项列表而不仅仅是一个。创建一个新的
TodoList
组件来容纳Todo
组件,并在其模板中使用 t-foreach。考虑如何使用
t-key
指令进行键控。
6. 添加一个待办事项¶
到目前为止,我们列表中的待办事项是硬编码的。让我们通过允许用户向列表中添加待办事项使其更加有用。
Exercise
在任务列表上方添加一个输入框,占位符为 输入新任务。
添加一个 event handler 在
keyup
事件上命名为addTodo
。实现
addTodo
函数来检查是否按下了回车键 (ev.keyCode === 13
),如果是的话,使用输入框当前的内容创建一个新的待办事项,并清空输入框中的所有内容。确保todo具有唯一的id。它可以只是一个计数器,在每个todo上递增。
将待办事项列表包裹在
useState
钩子中,让 Owl 知道当列表被修改时应该更新 UI。奖励分:如果输入为空,则不执行任何操作。
this.todos = useState([]);
另请参阅
7. 聚焦输入框¶
让我们看看如何使用 t-ref 和 useRef 来访问 DOM。
Exercise
8. 切换待办事项¶
现在,让我们添加一个新功能:将待办事项标记为已完成。这实际上比人们想象的要棘手。状态的所有者与显示状态的组件不同。因此, Todo
组件需要向其父组件传达待办事项状态需要切换的信息。一种经典的方法是使用 callback prop <https://github.com/odoo/owl/blob/master/doc/reference/props.md#binding-function-props>
_ toggleState
。
Exercise
在任务的id之前添加一个带有属性
type="checkbox"
的输入框,如果状态done
为真,则必须选中。小技巧
QWeb 不会创建使用
t-att
指令计算的属性,如果它的值为假值。添加一个回调属性
toggleState
.在
Todo
组件的输入框上添加一个click
事件处理程序,并确保它使用 todo id 调用toggleState
函数。让它工作起来!
9. 删除待办事项¶
最后一步是让用户删除待办事项。
Exercise
添加一个新的回调属性
removeTodo
.在
Todo
组件的模板中插入<span class="fa fa-remove"/>
。每当用户点击它时,它应该调用
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);
}
10. 通用卡片与插槽¶
Owl 有一个强大的 slot 系统,允许您编写通用组件。这对于在界面的不同部分之间因式分解常见布局非常有用。
Exercise
在
Counter
和Todolist
组件之间插入一个新的Card
组件。使用以下 Bootstrap HTML 结构来创建卡片:<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>
此组件应该有两个插槽:一个用于标题,一个用于内容(默认插槽)。应该可以按照以下方式使用
Card
组件:<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
插槽。