Odoo13表单视图中自定义widget如何正确访问其他字段修改样式
Odoo 13 跨字段修改样式规范实现
你之前用的两种方案都有明确缺陷:
- 链式读取
__parentedParent这类双下划线开头的属性属于访问框架私有变量,组件层级会随视图结构调整、版本迭代发生变化,属于完全不可维护的硬编码写法,改个视图嵌套结构就会直接报错。 - 全局选择器
$("[name='field_name']")加类失效的核心原因有两个:一是全局选择可能匹配到页面其他位置(比如子表、其他弹窗)的同名字段,找错节点;二是你选到的是字段内部的input/文本节点,Odoo字段更新时会重绘内部DOM,你加的类会被直接清空,同时字段根节点本身的默认样式优先级更高,内层加的text-danger不会生效。
正确实现方式
Odoo 13的表单组件本身提供了公开API获取同表单内的其他字段实例,不需要爬私有属性,也不需要全局查DOM。
- 等表单渲染完成后,通过表单渲染器的标准方法拿到目标字段的组件实例
把下面逻辑写到你的timer自定义widget的生命周期里即可:async start() { await this._super(...arguments); // 获取当前所在的表单渲染器实例,兼容字段嵌套在group、page里的场景 const formRenderer = this.findAncestor(parent => parent._name === 'FormRenderer'); // 等待表单所有字段挂载完成 await formRenderer._areToolsReady(); // 拿到time_given字段的组件实例 this.timeGivenFieldIns = formRenderer.getFieldComponent('time_given'); // 绑定你自己的计时器更新回调 this.bindTimerCheck(); } - 在计时器每次更新值的回调里,直接操作字段实例的根DOM节点修改样式,不会被框架重绘覆盖:
bindTimerCheck() { // 你自己原有计时逻辑的回调,每次timer值变动触发 const checkRemainTime = () => { if (!this.timeGivenFieldIns?.el) return; const remain = this.record.data.time_given - this.record.data.timer; // 直接给字段组件根节点加/删类,作用范围覆盖整个字段,不会被重绘清除 this.timeGivenFieldIns.el.classList.toggle('text-danger', remain <= 30); } // 首次加载执行一次,后面每次计时器跳动执行 checkRemainTime(); // 下面替换成你自己的计时器触发事件即可 this.el.addEventListener('timer-tick', checkRemainTime); }
补充说明
- 拿到字段实例后,除了加样式,你要修改字段提示文本、临时修改字段属性都可以直接调用实例的公开方法,比手动操作DOM稳定得多。
- 如果是在One2many子表的行内widget里用,把查找祖先的判断改成匹配
ListRenderer即可,逻辑完全一致。 - 不要在widget初始化阶段(init、willStart)就去查找其他字段,这时候其他字段还没完成渲染,会拿到空值。
内容的提问来源于stack exchange,提问作者Elnar Sh
相关产品推荐
相关产品推荐

