第三章:字段和視圖?

在上一章中,我們學習了一系列技能,包括如何創建和使用服務,使用布局組件,使儀表板可翻譯,并延遲加載像Chart.js這樣的JavaScript庫?,F在,讓我們繼續學習如何創建新的字段和視圖。

../../../_images/previously_learned1.svg

這是我們在 第二章:Odoo Web框架 結束時發現 JavaScript web 框架的進展。?

字段和視圖是Odoo用戶界面中最重要的概念之一。它們是許多重要用戶交互的關鍵,因此應該完美地工作。

在 JavaScript 框架的上下文中,字段是專門用于可視化/編輯給定記錄的特定字段的組件。

例如,一個(Python)模型可以定義一個字符字段,該字段將由字段組件“CharField”表示。

一個字段組件基本上只是在 fields registry 中注冊的組件。字段組件可以定義一些額外的靜態鍵(元數據),例如 displayNamesupportedTypes ,以及最重要的一個: extractProps ,它準備了由 CharField 接收的基本 props。

Example

讓我們討論一個 CharField 的簡化實現。

首先,這是模板:

<t t-name="web.CharField" owl="1">
    <t t-if="props.readonly">
        <span t-esc="formattedValue" />
    </t>
    <t t-else="">
        <input
            class="o_input"
            t-att-type="props.isPassword ? 'password' : 'text'"
            t-att-placeholder="props.placeholder"
            t-on-change="updateValue"
         />
    </t>
</t>

它具有只讀模式和編輯模式,后者是帶有一些屬性的輸入?,F在,這是JavaScript代碼:

export class CharField extends Component {
    get formattedValue() {
        return formatChar(this.props.value, { isPassword: this.props.isPassword });
    }

    updateValue(ev) {
       let value = ev.target.value;
       if (this.props.shouldTrim) {
           value = value.trim();
       }
       this.props.update(value);
    }
}

CharField.template = "web.CharField";
CharField.displayName = _lt("Text");
CharField.supportedTypes = ["char"];

CharField.extractProps = ({ attrs, field }) => {
    return {
        shouldTrim: field.trim && !archParseBoolean(attrs.password),
        maxLength: field.size,
        isPassword: archParseBoolean(attrs.password),
        placeholder: attrs.placeholder,
    };
};

registry.category("fields").add("char", CharField);

有幾個重要的事情需要注意:

  • CharFieldprops 中接收到它的(原始)值。在顯示之前,需要對其進行格式化。

  • 它在其props中接收一個 update 函數,該函數由字段用于通知狀態所有者該字段的值已更改。請注意,字段不會(也不應該)維護其值的本地狀態。每當更改已應用時,它將通過props的方式返回(可能在onchange之后)。

  • 它定義了一個 extractProps 函數。這是一個將通用標準屬性轉換為視圖特定專用屬性的步驟,這些屬性對組件非常有用。這使得組件具有更好的 API,并且可能使其可重用。

字段必須在“字段”注冊表中注冊。一旦完成,它們可以在某些視圖(即: form , list , kanban )中使用 widget 屬性。

Example

<field name="preview_moves" widget="account_resequence_widget"/>

目標

../../../_images/overview_03.png

本章節中每個練習的解決方案都托管在 官方Odoo教程存儲庫 中。

1. image_preview 字段?

網站上的每個新訂單都將被創建為 awesome_tshirt.order 。此模型具有 image_url 字段(類型為 char ),目前僅以字符串形式可見。我們希望在表單視圖中能夠看到它。

為了完成此任務,我們需要創建一個新的字段組件 image_preview 。該組件的規格如下:在只讀模式下,如果字段已設置,則僅為帶有正確 src 的圖像標簽;在編輯模式下,它也像經典的 char 字段一樣運行(您可以通過將其傳遞給 props 在模板中使用 CharField )。應該顯示一個 input ,其中包含字段的文本值,以便進行編輯。

Exercise

  1. 創建一個新的 ImagePreview 組件并在模板中使用 CharField 組件。您可以使用 t-propsImagePreview 接收到的 props 傳遞給 CharField 。

  2. 在正確的 注冊表 中注冊您的字段。

  3. 通過設置“widget”屬性,更新表單視圖的結構以使用您的新字段。

注解

雖然可以通過繼承 CharField 來解決這個練習,但是這個練習的目標是從頭開始創建一個字段。

../../../_images/image_field.png

另請參閱

代碼: CharField

2. 改進 image_preview 字段?

Exercise

我們想要改進上一個任務的字段,以幫助員工識別需要執行某些操作的訂單。特別是,如果訂單上沒有指定圖像URL,我們想要以紅色顯示警告”缺少T恤設計”。

../../../_images/missing_image.png

3. 自定義字段組件?

讓我們看看如何使用繼承來擴展現有組件。

任務模型上有一個只讀的布爾字段 is_late 。在列表/看板/視圖上看到這個信息會很有用。然后,假設我們想在它被設置為true時在旁邊添加一個紅色的單詞“Late!”。

Exercise

  1. 創建一個繼承自 BooleanField 的新 LateOrderBoolean 字段。 LateOrderBoolean 的模板也可以從 BooleanField 模板中 繼承 。

  2. 在列表/看板/表單視圖中使用它。

  3. 按照要求修改它,在旁邊添加一個紅色的“遲到”字樣。

../../../_images/late_field.png

4. 針對某些客戶的消息?

Odoo表單視圖支持 widget API,它類似于字段,但更通用。它可用于在表單視圖中插入任意組件。讓我們看看如何使用它。

Exercise

為了實現高效的工作流程,我們希望在表單視圖中顯示一個帶有一些信息的消息/警告框,具體的消息取決于某些條件:

  • 如果 image_url 字段未設置,則應顯示“無圖像”。

  • 如果訂單金額超過100歐元,則應顯示“添加促銷材料”。

  • 確保您的小部件實時更新。

../../../_images/warning_widget.png

5. 使用 markup?

讓我們看看如何在模板中顯示原始HTML。以前有一個 t-raw 指令,它會將任何東西都輸出為HTML。這是不安全的,已經被一個 t-out <https://github.com/odoo/owl/blob/master/doc/reference/templates.md#outputting-data>`_指令所取代,它的作用類似于 `t-esc ,除非數據已經明確地使用 markup 函數標記。

Exercise

  1. 修改前一個練習,將“image”和“material”這兩個詞加粗。

  2. 警告應該被標記,模板應該被修改為使用 t-out 。

注解

這是一個 t-out 的安全使用示例,因為字符串是靜態的。

../../../_images/warning_widget2.png

6. 在控制面板中添加按鈕?

視圖是Odoo中最重要的組件之一:它們允許用戶與其數據進行交互。讓我們討論一下Odoo視圖的設計。

Odoo視圖的強大之處在于它們聲明了特定屏幕應如何使用XML文檔(通常命名為“arch”,即architecture的縮寫)工作。此描述可以通過服務器端的xpath進行擴展/修改。然后,瀏覽器加載該文檔,解析它(這是一個花哨的詞,表示它提取有用的信息),然后相應地表示數據。

Example

arch 文檔是視圖特定的。以下是如何定義 graph 視圖或 calendar 視圖的示例:

<graph string="Invoices Analysis" type="line" sample="1">
    <field name="product_categ_id"/>
    <field name="price_subtotal" type="measure"/>
</graph>

<calendar string="Leads Generation" create="0" mode="month" date_start="activity_date_deadline" color="user_id" hide_time="true" event_limit="5">
    <field name="expected_revenue"/>
    <field name="partner_id" avatar_field="avatar_128"/>
    <field name="user_id" filters="1" invisible="1"/>
</calendar>

視圖是由具有一些特定鍵的對象在視圖注冊表中定義的。

  • type : 視圖的(基本)類型(例如, form , list …)。

  • display_name : 在視圖切換器中顯示的工具提示內容。

  • icon : 在視圖切換器中使用哪個圖標。

  • multiRecord : 視圖是否應該管理單個記錄或一組記錄。

  • Controller : 渲染視圖所使用的組件(最重要的信息)。

Example

這是一個最小的 Hello 視圖,它不顯示任何內容:

/** @odoo-module */

import { registry } from "@web/core/registry";

export const helloView = {
   type: "hello",
   display_name: "Hello",
   icon: "fa fa-picture-o",
   multiRecord: true,
   Controller: Component,
};

registry.category("views").add("hello", helloView);

大多數(或全部?)Odoo視圖共享一個通用的架構:

../../../_images/view_architecture.svg

視圖描述可以定義一個 props 函數,該函數接收標準的 props,并計算具體視圖的基本 props。 props 函數只執行一次,可以被視為某種工廠。它有助于解析 arch XML 文檔,并允許視圖被參數化(例如,它可以返回將用作 Renderer 的 Renderer 組件),但這樣可以輕松地自定義子視圖使用的特定渲染器。

這些屬性將在傳遞給控制器之前進行擴展。特別是,搜索屬性(domain/context/groupby)將被添加。

然后,根組件,通常稱為“控制器”,協調一切。它使用通用的“布局”組件(添加控制面板),實例化一個“模型”,并在“布局”默認插槽中使用“渲染器”組件。 模型 負責加載和更新數據, 渲染器 應處理所有渲染工作以及所有用戶交互。

實際上,一旦T恤訂單被打印,我們需要打印一個標簽放在包裹上。為此,讓我們在訂單表單視圖控制面板中添加一個按鈕,該按鈕將調用一個模型方法。

有一個專門用于調用模型方法的服務: orm_service ,位于 core/orm_service.js 。它提供了一種調用常見模型方法的方式,以及一個通用的 call(model, method, args, kwargs) 方法。

Example

setup() {
    this.orm = useService("orm");
    onWillStart(async () => {
        // will read the fields 'id' and 'descr' from the record with id=3 of my.model
        const data = await this.orm.read("my.model", [3], ["id", "descr"]);
        // ...
    });
}

Exercise

  1. 創建一個擴展了Web表單視圖的自定義表單視圖,并將其注冊為 awesome_tshirt.order_form_view 。

  2. 在表單視圖的arch中添加 js_class 屬性,這樣Odoo就會加載它。

  3. 創建一個新模板,繼承表單控制器模板,在創建按鈕后添加一個按鈕。

  4. 添加一個按鈕。點擊該按鈕應該調用模型 awesome_tshirt.order 中的方法 print_label 并傳入正確的id。注意: print_label 是一個模擬方法,它只在日志中顯示一條消息。

  5. 如果當前訂單處于“創建”模式(即尚不存在),則應禁用該按鈕。

  6. 如果客戶已正確設置并且任務階段為“已打印”,則應將按鈕顯示為主要按鈕。否則,它將顯示為次要按鈕。

  7. 加分項:點擊兩次按鈕不應觸發2個RPC。

../../../_images/form_button.png

7. 自動重新加載看板視圖?

Bafien很不高興:他想在外部顯示器上看到T恤訂單的看板視圖,但視圖需要保持最新狀態。他厭倦了每30秒點擊 刷新 圖標,所以他委托你找到一種自動執行的方法。

就像之前的練習一樣,這種定制需要創建一個新的JavaScript視圖。

Exercise

  1. 擴展看板視圖/控制器,使其每分鐘重新加載數據。

  2. 在視圖注冊表中注冊它,名稱為 awesome_tshirt.autoreloadkanban 。

  3. 在看板視圖的arch中使用它(使用 js_class 屬性)。

重要

如果您使用 setInterval 或類似的東西,請確保在組件卸載時正確取消它。否則,您將引入內存泄漏。