这是view系列的第六篇文章,我们学习用js_class来扩展view
学习odoo的一大窍门就是抄,因为odoo本身就是一个庞大的代码库,抄过来,改改就能用
搜索js_class,出现一堆结果,随便看一个
<form string="Account Entry" js_class="account_move_form">
搜索account_move_form 找到对应的js文件
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { createElement, append } from "@web/core/utils/xml";
import { Notebook } from "@web/core/notebook/notebook";
import { formView } from "@web/views/form/form_view";
import { FormCompiler } from "@web/views/form/form_compiler";
import { FormRenderer } from "@web/views/form/form_renderer";
import { FormController } from '@web/views/form/form_controller';
import { useService } from "@web/core/utils/hooks";
export class AccountMoveController extends FormController {
setup() {
super.setup();
this.account_move_service = useService("account_move");
}
async deleteRecord() {
if ( !await this.account_move_service.addDeletionDialog(this, this.model.root.resId)) {
return super.deleteRecord(...arguments);
}
}
};
export class AccountMoveFormNotebook extends Notebook {
async changeTabTo(page_id) {
if (this.props.onBeforeTabSwitch) {
await this.props.onBeforeTabSwitch(page_id);
}
this.state.currentPage = page_id;
}
}
AccountMoveFormNotebook.template = "account.AccountMoveFormNotebook";
AccountMoveFormNotebook.props = {
...Notebook.props,
onBeforeTabSwitch: { type: Function, optional: true },
}
export class AccountMoveFormRenderer extends FormRenderer {
async saveBeforeTabChange() {
if (this.props.record.isInEdition && await this.props.record.isDirty()) {
const contentEl = document.querySelector('.o_content');
const scrollPos = contentEl.scrollTop;
await this.props.record.save();
if (scrollPos) {
contentEl.scrollTop = scrollPos;
}
}
}
}
AccountMoveFormRenderer.components = {
...FormRenderer.components,
AccountMoveFormNotebook: AccountMoveFormNotebook,
}
export class AccountMoveFormCompiler extends FormCompiler {
compileNotebook(el, params) {
const originalNoteBook = super.compileNotebook(...arguments);
const noteBook = createElement("AccountMoveFormNotebook");
for (const attr of originalNoteBook.attributes) {
noteBook.setAttribute(attr.name, attr.value);
}
noteBook.setAttribute("onBeforeTabSwitch", "() => __comp__.saveBeforeTabChange()");
const slots = originalNoteBook.childNodes;
append(noteBook, [...slots]);
return noteBook;
}
}
export const AccountMoveFormView = {
...formView,
Renderer: AccountMoveFormRenderer,
Compiler: AccountMoveFormCompiler,
Controller: AccountMoveController,
};
registry.category("views").add("account_move_form", AccountMoveFormView);
通过上面的代码,我们看到它扩展了Renderer,Compiler,Controller三个组件,
而其他的属性通过…formView 解构赋值,值得注意的是这一句一定要放在最上面,因为后面的值会覆盖前面的,最后将这个新的结构体注册到注册表中。
我们看看原来的formView是怎么写的,从代码看,formView是一个字典,包含了诸多属性
type: "form", 视图类型
display_name: "Form", 显示名称
multiRecord: false, 是否支持多行
searchMenuTypes: [], 搜索菜单类型,什么鬼?
Controller: FormController, Controller组件(重要)
Renderer: FormRenderer, renderer组件(重要)
ArchParser: FormArchParser, arch解析器,(重要)
Model: RelationalModel, model (重要)
Compiler: FormCompiler, 编译器(将xml文件编译成qweb模板,重要)
buttonTemplate: "web.FormView.Buttons", button模板
通常对视图的扩展就是修改它的Controller和Render组件。修改完之后生成一个新的字典,然后生成一个新的名字注册到注册表中。 然后在js_class中指定这个名称,前端解析xml的时候就会根据这个js_class名称去注册表中查找对应的信息,然后渲染页面。
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { RelationalModel } from "@web/model/relational_model/relational_model";
import { FormRenderer } from "./form_renderer";
import { FormArchParser } from "./form_arch_parser";
import { FormController } from "./form_controller";
import { FormCompiler } from "./form_compiler";
export const formView = {
type: "form",
display_name: "Form",
multiRecord: false,
searchMenuTypes: [],
Controller: FormController,
Renderer: FormRenderer,
ArchParser: FormArchParser,
Model: RelationalModel,
Compiler: FormCompiler,
buttonTemplate: "web.FormView.Buttons",
props: (genericProps, view) => {
const { ArchParser } = view;
const { arch, relatedModels, resModel } = genericProps;
const archInfo = new ArchParser().parse(arch, relatedModels, resModel);
return {
...genericProps,
Model: view.Model,
Renderer: view.Renderer,
buttonTemplate: genericProps.buttonTemplate || view.buttonTemplate,
Compiler: view.Compiler,
archInfo,
};
},
};
registry.category("views").add("form", formView);
js文件
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { formView } from "@web/views/form/form_view";
import { FormController } from '@web/views/form/form_controller';
export class DemoFormController extends FormController {
};
DemoFormController.template = "crax_demo.demo_form_controller"
export const CraxDemoFormView = {
...formView,
Controller: DemoFormController,
};
registry.category("views").add("crax_demo_form_view", CraxDemoFormView);
xml文件
<templates>
<t t-name="crax_demo.demo_form_controller" t-inherit="web.FormView" t-inherit-mode="primary">
<xpath expr="//div[hasclass('o_form_view_container')]" position="inside">
<div style="color:red;">
hello world
div>
xpath>
t>
templates>
这里只是简单的在o_form_view_container里面加了一个红色的hello,world!
例子很简单,但是套路讲明白了。