Tùy chỉnh một loại view

Kế thừa một view hiện có

Giả sử chúng ta cần tạo một phiên bản tùy chỉnh của một view thông thường. Ví dụ, một kanban view có thêm một widget dạng ribbon phía trên (để hiển thị một số thông tin tùy chỉnh cụ thể). Trong trường hợp đó, việc này có thể được thực hiện qua vài bước:

  1. Kế thừa controller/renderer/model của kanban và đăng ký nó trong registry của view.

    custom_kanban_controller.js
    /** @odoo-module */
    
    import { KanbanController } from "@web/views/kanban/kanban_controller";
    import { kanbanView } from "@web/views/kanban/kanban_view";
    import { registry } from "@web/core/registry";
    
    // the controller usually contains the Layout and the renderer.
    class CustomKanbanController extends KanbanController {
        // Your logic here, override or insert new methods...
        // if you override setup(), don't forget to call super.setup()
    }
    
    CustomKanbanController.template = "my_module.CustomKanbanView";
    
    export const customKanbanView = {
        ...kanbanView, // contains the default Renderer/Controller/Model
        Controller: CustomKanbanController,
    };
    
    // Register it to the views registry
    registry.category("views").add("custom_kanban", customKanbanView);
    

    Trong kanban tùy chỉnh của chúng ta, chúng ta đã định nghĩa một template mới. Chúng ta có thể kế thừa template của controller kanban và thêm các phần template của mình, hoặc chúng ta có thể định nghĩa một template hoàn toàn mới.

    custom_kanban_controller.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <templates>
        <t t-name="my_module.CustomKanbanView" t-inherit="web.KanbanView">
            <xpath expr="//Layout" position="before">
                <div>
                    Hello world !
                </div>
            </xpath>
        </t>
    </templates>
    
  2. Sử dụng view với thuộc tính js_class trong arch.

    <kanban js_class="custom_kanban">
        <templates>
            <t t-name="kanban-box">
                <!--Your comment-->
            </t>
        </templates>
    </kanban>
    

Khả năng mở rộng view là vô hạn. Mặc dù ở đây chúng ta chỉ mới mở rộng controller, bạn cũng có thể mở rộng renderer để thêm các nút mới, thay đổi cách các bản ghi được hiển thị, hoặc tùy chỉnh dropdown, cũng như mở rộng các component khác như model và buttonTemplate.

Tạo một view mới từ đầu

Việc tạo một view mới là một chủ đề nâng cao. Hướng dẫn này chỉ nêu bật những bước thiết yếu.

  1. Tạo controller.

    Vai trò chính của một controller là hỗ trợ sự phối hợp giữa các component khác nhau của một view, chẳng hạn như Renderer, Model, và Layout.

    beautiful_controller.js
    /** @odoo-module */
    
    import { Layout } from "@web/search/layout";
    import { useService } from "@web/core/utils/hooks";
    import { Component, onWillStart, useState} from "@odoo/owl";
    
    export class BeautifulController extends Component {
        setup() {
            this.orm = useService("orm");
    
            // The controller create the model and make it reactive so whenever this.model is
            // accessed and edited then it'll cause a rerendering
            this.model = useState(
                new this.props.Model(
                    this.orm,
                    this.props.resModel,
                    this.props.fields,
                    this.props.archInfo,
                    this.props.domain
                )
            );
    
            onWillStart(async () => {
                await this.model.load();
            });
        }
    }
    
    BeautifulController.template = "my_module.View";
    BeautifulController.components = { Layout };
    

    Template của Controller hiển thị control panel cùng với Layout và cả renderer.

    beautiful_controller.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <templates xml:space="preserve">
        <t t-name="my_module.View">
            <Layout display="props.display" className="'h-100 overflow-auto'">
                <t t-component="props.Renderer" records="model.records" propsYouWant="'Hello world'"/>
            </Layout>
        </t>
    </templates>
    
  2. Tạo renderer.

    Chức năng chính của một renderer là tạo ra biểu diễn trực quan của dữ liệu bằng cách render view chứa các bản ghi.

    beautiful_renderer.js
    import { Component } from "@odoo/owl";
    export class BeautifulRenderer extends Component {}
    
    BeautifulRenderer.template = "my_module.Renderer";
    
    beautiful_renderer.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <templates xml:space="preserve">
        <t t-name="my_module.Renderer">
            <t t-esc="props.propsYouWant"/>
            <t t-foreach="props.records" t-as="record" t-key="record.id">
                // Show records
            </t>
        </t>
    </templates>
    
  3. Tạo model.

    Vai trò của model là lấy về và quản lý tất cả dữ liệu cần thiết trong view.

    beautiful_model.js
    /** @odoo-module */
    
    import { KeepLast } from "@web/core/utils/concurrency";
    
    export class BeautifulModel {
        constructor(orm, resModel, fields, archInfo, domain) {
            this.orm = orm;
            this.resModel = resModel;
            // We can access arch information parsed by the beautiful arch parser
            const { fieldFromTheArch } = archInfo;
            this.fieldFromTheArch = fieldFromTheArch;
            this.fields = fields;
            this.domain = domain;
            this.keepLast = new KeepLast();
        }
    
        async load() {
            // The keeplast protect against concurrency call
            const { length, records } = await this.keepLast.add(
                this.orm.webSearchRead(this.resModel, this.domain, [this.fieldsFromTheArch], {})
            );
            this.records = records;
            this.recordsLength = length;
        }
    }
    

    Ghi chú

    Đối với các trường hợp nâng cao, thay vì tạo một model từ đầu, bạn cũng có thể sử dụng RelationalModel, vốn được các view khác sử dụng.

  4. Tạo arch parser.

    Vai trò của arch parser là phân tích cú pháp (parse) arch của view để view có thể truy cập được thông tin.

    beautiful_arch_parser.js
    /** @odoo-module */
    
    import { XMLParser } from "@web/core/utils/xml";
    
    export class BeautifulArchParser extends XMLParser {
        parse(arch) {
            const xmlDoc = this.parseXML(arch);
            const fieldFromTheArch = xmlDoc.getAttribute("fieldFromTheArch");
            return {
                fieldFromTheArch,
            };
        }
    }
    
  5. Tạo view và kết hợp tất cả các phần lại với nhau, sau đó đăng ký view vào registry của các view.

    beautiful_view.js
    /** @odoo-module */
    
    import { registry } from "@web/core/registry";
    import { BeautifulController } from "./beautiful_controller";
    import { BeautifulArchParser } from "./beautiful_arch_parser";
    import { BeautifylModel } from "./beautiful_model";
    import { BeautifulRenderer } from "./beautiful_renderer";
    
    export const beautifulView = {
        type: "beautiful",
        display_name: "Beautiful",
        icon: "fa fa-picture-o", // the icon that will be displayed in the Layout panel
        multiRecord: true,
        Controller: BeautifulController,
        ArchParser: BeautifulArchParser,
        Model: BeautifulModel,
        Renderer: BeautifulRenderer,
    
        props(genericProps, view) {
            const { ArchParser } = view;
            const { arch } = genericProps;
            const archInfo = new ArchParser().parse(arch);
    
            return {
                ...genericProps,
                Model: view.Model,
                Renderer: view.Renderer,
                archInfo,
            };
        },
    };
    
    registry.category("views").add("beautifulView", beautifulView);
    
  6. Khai báo view trong arch.

    ...
    <record id="my_beautiful_view" model="ir.ui.view">
      <field name="name">my_view</field>
      <field name="model">my_model</field>
      <field name="arch" type="xml">
          <beautiful fieldFromTheArch="res.partner"/>
      </field>
    </record>
    ...