跳到主要内容

Odoo 16 OWL Field 组件开发指南

在 Odoo 16 中,Field 组件基于 OWL 2 (Odoo Web Library) 框架构建。与旧版本不同,现在的字段开发更贴近现代前端框架的组件化模式。

一、 Field 组件的五个核心组成部分

开发一个完整的字段组件,通常需要以下五个部分的协同工作:

1. 逻辑控制 (JavaScript)

这是组件的大脑。你需要定义一个继承自 OWL Component 的类。

  • 核心任务:管理生命周期、处理用户交互、调用更新逻辑。
  • 静态属性
    • supportedTypes: 数组,声明支持的后端字段类型(如 char, integer, float)。
    • template: 字符串,关联 XML 模板名称。
    • extractProps: 静态函数,负责将 XML 视图中的 options 属性解析并传递给组件的 propsextractProps接收参数的Object中只有fieldattrs两个key,其中xml视图中的options属性则属于attrs(处理这部分的源码位于addons/web/static/src/views/fields/field.js -> get fieldComponentProps())。

2. 视图模板 (XML)

定义组件的 HTML 结构。

  • 模式区分:通常使用 t-if="props.readonly" 来区分只读模式(展示文本)和编辑模式(展示输入控件)。
  • 引用绑定:使用 t-ref 配合 JavaScript 中的 useReference 或 Hook 来操作具体的 DOM。

3. 样式定义 (SCSS)

定义组件的视觉外观。

  • 命名规范:通常建议以 .o_field_[widget_name] 作为根命名空间,以防样式冲突。
  • 自适应:利用 Odoo 后端的 CSS 变量处理主题色和间距。

4. 注册 (Registry)

将你的 JavaScript 类告诉 Odoo 系统。

  • 位置:注册到 fields 类别中。
    import { registry } from "@web/core/registry";
    registry.category("fields").add("my_widget_name", MyFieldComponent);

5. 资源声明 (Manifest)

在模块的 __manifest__.py 中通过 assets 字段引入相关文件,确保浏览器能加载到这些资源。


二、 Odoo 16 原生标准化支持

Odoo 16 提供了丰富的内置工具,开发时应优先使用这些“标准化”方案,以保持系统一致性:

1. 标准属性 (standardFieldProps)

所有被视图加载的字段组件都会自动接收到一套标准 Props:

  • id: String
  • name: String,字段名称
  • readonly: Boolean,标识当前只读状态
  • record: Object,当前整条记录的代理对象(DataPoint),可用于获取其他字段的值
  • type: String,类型
  • update: Function,更新props.value的函数
  • value: 后端存储的当前值
  • decorations: Object,控制动态显色的对象(例如:decoration-success="state == 'done'")
  • setDirty: Function,用于通知系统用户修改了数据

2. 核心 Hook:useInputField

对于所有“输入框”类型的字段,这是必用的 Hook。它自动处理了:

  • 临时值管理:用户输入时的中间状态,失焦或回车时触发更新(commitChanges->props.update())。
  • 变更管理:通过事件管理监听change事件,在数据发生变更时先通过params.parse()尝试进行数据转换,如果数据合法,则触发更新(component.props.update())。
  • 解析与格式化:通过配置 parsegetValue 参数自动处理数据转换。
    • parse: 更新前处理数据的函数
    • getValue: 格式化输出的函数
  • 脏数据处理:在onChangecommitChanges函数中尝试对数据进行params.parse()处理时,如果抛出异常,则视为数据不合法,调用component.props.record.setInvalidField(component.props.name)来通知系统该字段数据不合法。

3. 格式化与解析工具

  • @web/views/fields/formatters: 提供 formatInteger, formatFloat, formatMonetary 等,将后端数据转为本地化字符串(如加千分位)。
  • @web/views/fields/parsers: 提供 parseInteger, parseFloat 等,将用户输入的字符串还原为后端数值。

三、 扩展与开发最佳实践

1. 扩展方式的选择

  • 创建新 Widget:当逻辑完全不同时,定义新类并注册新名称。
  • 打补丁 (Patching):如果你想修改 Odoo 原生字段(如 char 字段)在全系统的表现,使用 patch 工具函数。
    import { patch } from "@web/core/utils/patch";
    import { CharField } from "@web/views/fields/char/char_field";
    patch(CharField.prototype, "my_custom_patch", { ... });

2. 数据流原则

  • 单向数据流:永远不要直接修改 this.props.value
  • 唯一更新入口:始终通过 this.props.record.update({ [this.props.name]: newValue }) 或更简单的 this.props.update(newValue) 来修改数据。

3. 性能注意点

  • Getter 缓存:由于 OWL 模板会频繁重绘,如果在 get 属性中执行了复杂的循环或逻辑,建议配合 onWillUpdateProps 进行缓存处理。
  • 异步处理:如果字段需要调用 RPC 获取数据,务必在 onWillStartonWillUpdateProps 中处理异步逻辑。

4. 错误验证

  • useInputFieldparse 方法中,如果输入非法,可以直接抛出异常。Odoo 的视图控制器会自动捕获异常并给字段标记红色边框,同时阻止记录保存。

四、 最小组件实现代码模板

/** @odoo-module **/
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { useInputField } from "@web/views/fields/input_field_hook";

export class MyCustomField extends Component {
setup() {
// 使用标准化 Hook 处理输入逻辑
useInputField({
getValue: () => this.props.value || "",
parse: (v) => v.trim(), // 示例逻辑:自动去空格
});
}
}

// 关联模板
MyCustomField.template = "my_module.MyCustomFieldTemplate";
// 继承标准属性
MyCustomField.props = { ...standardFieldProps };
// 声明支持类型
MyCustomField.supportedTypes = ["char", "text"];

// 注册到系统
registry.category("fields").add("my_custom_widget", MyCustomField);

最后更新日期:2026年1月26日