如何使用js_class扩展Odoo视图?

发布时间 2023-09-27 12:41:29作者: CrossPython

Odoo的主要功能之一是能够创建各种视图,允许数据记录以不同的形式展现并进行交互。目前提供的视图类型主要是有tree、form、kanban、calendar、pivot、graph、activity等。我们也可以创建自己需要的新类型,比如百度地图、大屏看板等,不过创建新的视图涉及的JS内容比较复杂,今天我们就不做介绍。今天我们将探讨如何在Odoo中对现有的视图类型进行扩展,以实现现有功能的增强。

在addons\web\static\src\webclient\actions\action_service.js中,有如下一段逻辑:

for (const [, type] of action.views) {
    if (type !== "search") {
        const arch = viewDescriptions[type].arch;
        const archDoc = domParser.parseFromString(arch, "text/xml").documentElement;
        const jsClass = archDoc.getAttribute("js_class");
        const view = viewRegistry.get(jsClass, false) || viewRegistry.get(type, false);
        if (view) {
            views.push(view);
        }
    }
}

  

这个说明只要不是search类型的视图,我们都可以通过js_class属性来指定一个新的视图,当然一般新的视图定义也是继承自原来的基础类型,只是增强部分功能。这里定义的增强可能只是js业务逻辑的变更,也可能会有UI界面上的变更。接下来我们通过实际的示例来看看这种设计效果。

现在我要在Form视图上的‘新建’按钮边上加一个新的按钮,并且点击按钮后会执行一段js代码。因为这里加按钮的话会改变UI,所以我们就先定义一个xml模板,继承基础的Form模板后增加新的按钮:

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
    <!-- 这里是继承原来的form视图的按钮模板,然后增加一个新的按钮,并且指定这个按钮点击时执行的js函数名称。
    这里新的模板名称t-name要记住,后面在js中会用到。新的js方法名称也要记住,后面在js中会用到。-->
    
    <t t-name="web.FormView.Jsbtn" t-inherit="web.FormView" t-inherit-mode="primary" owl="1">
        <xpath expr="//button[contains(@class, 'o_form_button_create')]" position="after">
            <button type="button" class="btn btn-outline-primary" data-hotkey="j" t-on-click.stop="onCallJs" t-att-disabled="state.isDisabled">
                ClickMe!
            </button>
        </xpath>
    </t>
    
</templates>

  

这里定义了一个新的模板,t-name指定了新模板名称,这个在后面的js中会要用到。t-inherit指明了我们是继承自web.FormView模板,所以原来UI是什么样子我们就不用关心,我们只关注通过xpath增加的部分。t-inherit-mode是说明继承的行为,primary是创建了一个新的子模板,extension表示更改了父模板,因为我们不希望影响其它的Form视图,只对新视图增加按钮,所以我们用的是primary。在定义button中通过t-on-click指定了按钮点击时执行的js方法,这个方法在后面的js源码中需要定义,并开发自己需要的业务逻辑。

接下来我们看新视图的js代码要怎么写:

/** @odoo-module **/

import { _lt } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { formView } from '@web/views/form/form_view';
import { FormController } from '@web/views/form/form_controller';
const Dialog = require('web.Dialog');

//因为新的视图控制对象增加了新的方法名称,所以我们要继承原来的FormController,生成一个新的控制对象。
export class AddJsButtonFormController extends FormController {
    setup() {
        super.setup()
    }
    //这个方法名称就是你在xml定义模板时,指定的t-on-click的方法名称
    async onCallJs() {
        //你可以在这个方法里写任何你想要的代码,比如弹出一个对话框
        var dialog = new Dialog(null, {
            title: this.model.root.data.name,
            size: 'medium',
            $content: $('<div/>', {
                html: 'hi,I am Odoog.'//这里就是添加你要显示的内容
            }),
            buttons: [{
                text: "Close",
                close: true
            }]
        });
        dialog.open();
    }
};

//这里指定新的模板名称,这个名称是在xml模板定义中的t-name指定。
AddJsButtonFormController.template = `web.FormView.Jsbtn`;

//下面是指定新视图对象,主要还是继承了原来的form视图对象,这里变更的只有两个内容,一个是按钮模板,另一个是新的控制对象。
export const AddJsButtonFormView = {
    ...formView,
    Controller: AddJsButtonFormController,
};

//给odoo注册一个新的视图名称,这个名称可以在任意的form定义中增加js_class="form_js_btn"即可。
registry.category("views").add("form_js_btn", AddJsButtonFormView);

  

因为我们是继承自Form视图,所以我们在js中也是继承跟form相关的对象,这里主要是form_view、form_controller,我们在FormController中增加了新的js方法,并且指定了新的模板名称。最后通过registry将新的视图名称进行了注册绑定。

最后我们把js和xml引入模块的资源包中,并安装模块。

'assets': {
        'web.assets_backend': [
            'og_form_js_button/static/src/js/*', #这里会导入新视图需要的js文件和xml模板文件
        ],
    },

  

当我们需要给某个Form视图增加按钮的时候,我们就可以通过xpath来对相应的视图增加js_class属性,如:

<odoo>
    <record id="res_partner_form_view_jsbtn" model="ir.ui.view">
        <field name="name">res.partner.form.js</field>
        <field name="model">res.partner</field>
        <field name="inherit_id" ref="base.view_partner_form"/>
        <field name="arch" type="xml">
            <xpath expr="//form" position="attributes">
                <attribute name="js_class">form_js_btn</attribute>
            </xpath>
        </field>
    </record>
</odoo>

  

我们给业务伙伴加了新的按钮,我们看显示效果如下:

 

当点击这个按钮时,会弹出一个对话框:

 

 

以上就是一个简单的关于js_class应用的实例。利用这个功能可以实现很多变态老板的变态需求,让我们在二开的路上继续前进!