【原創】Odoo開發文檔學習之:構建接口擴展(Building Interface Extensions)(邊Google翻譯邊學習)

  構建接口擴展(Building Interface Extensions)javascript

  本指南是關於爲Odoo的web客戶建立模塊。css

  要建立有Odoo的網站,請參見創建網站;要添加業務功能或擴展Odoo的現有業務系統,請參見構建模塊html

警告:
該指南須要如下知識:
Javascript 、jQuery、Underscore.js
同時也須要安裝 Odoo 和 Git。

  一個簡單的模型

  讓咱們從一個簡單的Odoo模塊開始,它包含基本的web組件配置,並讓咱們測試web框架。
  示例模塊能夠在線下載,可使用如下命令下載:java

$ git clone http://github.com/odoo/petstore

  這將在您執行命令的地方建立一個petstore文件夾。而後須要將該文件夾添加到Odoo的addons路徑中,建立一個新的數據庫並安裝oepetstore模塊。git

  若是您瀏覽petstore文件夾,您應該看到如下內容:github

oepetstore
|-- images
|   |-- alligator.jpg
|   |-- ball.jpg
|   |-- crazy_circle.jpg
|   |-- fish.jpg
|   `-- mice.jpg
|-- __init__.py
|-- oepetstore.message_of_the_day.csv
|-- __manifest__.py
|-- petstore_data.xml
|-- petstore.py
|-- petstore.xml
`-- static
    `-- src
        |-- css
        |   `-- petstore.css
        |-- js
        |   `-- petstore.js
        `-- xml
            `-- petstore.xml

  模塊已經包含了各類服務器定製。稍後咱們將回到這些內容,如今讓咱們關注與web相關的內容,在靜態文件夾(static)中。web

  在Odoo模塊的「web」端中使用的文件必須放置在靜態文件夾中,這樣它們就能夠在web瀏覽器中使用,而瀏覽器以外的文件也不能被瀏覽器獲取。src/css、src/js和src/xml子文件夾是常規的,並非絕對必要的。數據庫

  

oepetstore/static/css/petstore.css

  目前爲空,將爲寵物店(pet store)內容保留CSS。api

oepetstore/static/xml/petstore.xml

  大部分也是空的,將保存QWeb模板。數組

oepetstore/static/js/petstore.js

  最重要(也是最有趣的)部分,包含javascript應用程序的邏輯(或者至少是它的web瀏覽器端)。它如今應該是:

openerp.oepetstore = function(instance, local) { //特別注意:紅色部分在開發文檔中10.0版本中用odoo關鍵字,可是測試時沒法經過,必須是openerp,估計是還沒有徹底支持odoo關鍵字
    var _t = instance.web._t,
        _lt = instance.web._lt;
    var QWeb = instance.web.qweb;

    local.HomePage = instance.Widget.extend({
        start: function() {
            console.log("pet store home page loaded");
        },
    });

    instance.web.client_actions.add(
        'petstore.homepage', 'instance.oepetstore.HomePage');
}

  它只在瀏覽器的控制檯打印一個小消息。

  靜態文件夾中的文件,須要在模塊中定義,以便正確加載它們。src/xml中的全部內容都在__manifest . __中定義。在petstore.xml或相似的文件中定義或引用src/css和src/js的內容。

  

警告
全部的JavaScript文件都被鏈接和縮小以提升應用程序的加載時間。
其中一個缺點是,隨着單個文件的消失,調試變得更加困難,並且代碼的可讀性也大大下降。能夠經過啓用「開發者模式」來禁用此過程:
登陸到您的Odoo實例(默認用戶admin密碼admin)打開用戶菜單(在Odoo屏幕的右上角)並選擇Odoo,而後激活開發者模式:

   Odoo JavaScript單元

  Javascript沒有內置模塊。所以,在不一樣文件中定義的變量都會混合在一塊兒,並可能發生衝突。這引起了各類模塊模式,用於構建乾淨的名稱空間並限制命名衝突的風險。 Odoo框架使用一種這樣的模式來定義Web插件中的模塊,以便命名空間代碼和正確地命令其加載。

oepetstore/static/js/petstore.js

  文件中包含一個模塊聲明,代碼以下:

openerp.oepetstore = function(instance, local) {
    local.xxx = ...;
}

  在Odoo網站中,模塊被聲明爲在全局odoo(請改爲openerp)變量上設置的函數。該函數的名稱必須與模塊名稱(在這裏爲oeststore)相同,以便框架能夠找到它,並自動初始化它。

  當Web客戶端加載你的模塊時,它會調用根函數並提供兩個參數:

  第一個參數(instance)是Odoo Web客戶端的當前實例,它容許訪問由Odoo(網絡服務)定義的各類功能以及由內核或其餘模塊定義的對象。

  第二個參數(local)是您本身的本地名稱空間,由Web客戶端自動建立。應該能夠從模塊外部訪問的對象和變量(不管是由於Odoo Web客戶端須要調用它們,仍是由於其餘人可能想要定製它們)應該在該名稱空間內設置。

  類

  就像模塊同樣,而且與大多數面向對象的語言相反,JavaScript不會構建在classes中,儘管它提供了大體相同(若是是較低級別和更詳細的)機制。

  爲了簡單和開發人員友好,Odoo web提供了一個基於John Resig的簡單JavaScript繼承的類系統。

  經過調用odoo.web.Class()的extend()方法來定義新的類:

var MyClass = instance.web.Class.extend({
    say_hello: function() {
        console.log("hello");
    },
});

  extend()方法須要一個描述新類的內容(方法和靜態屬性)的字典。在這種狀況下,它只會有一個不帶參數的say_hello方法。

  類使用new運算符實例化:

var my_object = new MyClass();
my_object.say_hello();
// print "hello" in the console

  實例的屬性能夠經過如下方式 this 訪問:

var MyClass = instance.web.Class.extend({
    say_hello: function() {
        console.log("hello", this.name);
    },
});

var my_object = new MyClass();
my_object.name = "Bob";
my_object.say_hello();
// print "hello Bob" in the console

   經過定義init()方法,類能夠提供初始化程序來執行實例的初始設置。初始化程序接收使用新運算符時傳遞的參數:

var MyClass = instance.web.Class.extend({
    init: function(name) {
        this.name = name;
    },
    say_hello: function() {
        console.log("hello", this.name);
    },
});

var my_object = new MyClass("Bob");
my_object.say_hello();
// print "hello Bob" in the console

  也能夠經過在父類上調用extend()來建立現有(使用定義的)類的子類,如同子類Class()所作的那樣:

var MySpanishClass = MyClass.extend({
    say_hello: function() {
        console.log("hola", this.name);
    },
});

var my_object = new MySpanishClass("Bob");
my_object.say_hello();
// print "hola Bob" in the console

  當使用繼承覆蓋方法時,可使用this._super()調用原始方法:

var MySpanishClass = MyClass.extend({
    say_hello: function() {  //已覆蓋的方法
        this._super();       //調用父類中的原始方法,即「hello 。。。」
        console.log("translation in Spanish: hola", this.name);
    },
});

var my_object = new MySpanishClass("Bob");
my_object.say_hello();
// print "hello Bob \n translation in Spanish: hola Bob" in the console

  警告

  _super不是一個標準的方法,它被設置爲當前繼承鏈中的一個方法(若是有的話)。它只在方法調用的同步部分中定義,用於異步處理程序(在網絡調用或setTimeout回調以後)應該保留對其值的引用,所以不該經過如下方式訪問它:

// 如下調用會產生錯誤
say_hello: function () {
    setTimeout(function () {
        this._super();
    }.bind(this), 0);
}

// 如下方式正確
say_hello: function () {
    // 不能忘記 .bind()
    var _super = this._super.bind(this);
    setTimeout(function () {
        _super();
    }.bind(this), 0);
}

   Widgets基礎

  Odoo web 客戶端捆綁了jQuery以實現簡單的DOM操做。它比標準的W3C DOM2更有用,而且提供了更好的API,但不足以構成複雜的應用程序,致使難以維護。 很像面向對象的桌面UI工具包(例如Qt,Cocoa或GTK),Odoo Web使特定組件負責頁面的各個部分。在Odoo網站中,這些組件的基礎是Widget()類,它是專門處理頁面部分並顯示用戶信息的組件

  您的第一個Widget

  初始演示模塊已經提供了一個基本的widget:

local.HomePage = instance.Widget.extend({
    start: function() {
        console.log("pet store home page loaded");
    },
});

  它擴展了Widget()並重載了標準方法start(),它與以前的MyClass很像,如今作的不多。

   該行在文件末尾:

instance.web.client_actions.add(
    'petstore.homepage', 'instance.oepetstore.HomePage');

  將咱們的widget註冊爲客戶端操做。客戶端操做將在稍後解釋,如今這只是當咱們選擇 Pet Store ‣ Pet Store ‣ Home Page 菜單時,能夠調用和顯示咱們的窗口小部件。

  警告

  因爲該組件將從咱們的模塊外部調用,Web客戶端須要其「徹底限定(規範)」名稱,而不是任意名稱。

  顯示內容

  widget有不少方法和功能,但基礎很簡單:
  設置一個widget;
  格式化widget的數據;
    顯示widget。
  HomePage 的widget已經有了一個start()方法。該方法是常規widget生命週期的一部分,並在widget插入頁面後自動調用。咱們可使用它來顯示一些內容。
  全部widget都有一個$ el,表明它們負責的頁面部分(做爲jQuery對象)。應該在那裏插入widget內容。默認狀況下,$ el是一個空的<div>元素。
  若是用戶沒有內容(或沒有特定的樣式給它一個大小),那麼<div>元素一般對用戶是不可見的,這就是爲何當HomePage啓動時沒有任何內容顯示在頁面上。
  讓咱們使用jQuery向小部件的根元素添加一些內容:
local.HomePage = instance.Widget.extend({
    start: function() {
        this.$el.append("<div>Hello dear Odoo user!</div>");
    },
});

  當您打開 Pet Store ‣ Pet Store ‣ Home Page時,此消息將顯示。

  注意

  要刷新Odoo Web中加載的JavaScript代碼,您須要從新加載頁面(升級一下模塊)。沒有必要從新啓動Odoo服務器。

  HomePage Widget 由Odoo Web使用並自動管理。要學習如何從頭開始使用Widget,咱們來建立一個新Widget:

local.GreetingsWidget = instance.Widget.extend({
    start: function() {
        this.$el.append("<div>We are so happy to see you again in this menu!</div>");
    },
});

  如今咱們可使用GreetingsWidget的appendTo()方法將咱們的GreetingsWidget添加到主頁:

local.HomePage = instance.Widget.extend({
    start: function() {
        this.$el.append("<div>Hello dear Odoo user!</div>");
        var greeting = new local.GreetingsWidget(this);
        return greeting.appendTo(this.$el);
    },
});

  HomePage首先將其本身的內容添加到其DOM根目錄;

  HomePage而後實例化GreetingsWidget ;

  最後,它告訴GreetingsWidget將本身的部分插入到GreetingsWidget中。

  當調用appendTo()方法時,它會要求小部件(widget,如下將的小部件就是widget)將自身插入指定位置並顯示其內容。在調用appendTo()期間,將調用start()方法。

  要查看顯示界面下發生了什麼,咱們將使用瀏覽器的DOM Explorer。但首先讓咱們稍微修改咱們的小部件,以便經過向它們的根元素添加一個類來更輕鬆地找到它們的位置:

local.HomePage = instance.Widget.extend({
    className: 'oe_petstore_homepage',
    ...
});
local.GreetingsWidget = instance.Widget.extend({
    className: 'oe_petstore_greetings',
    ...
});

  若是您能夠找到DOM的相關部分(右鍵單擊文本而後檢查元素),它應該以下所示:

<div class="oe_petstore_homepage">
    <div>Hello dear Odoo user!</div>
    <div class="oe_petstore_greetings">
        <div>We are so happy to see you again in this menu!</div>
    </div>
</div>

  它清楚地顯示了由Widget()自動建立的兩個<div>元素,由於咱們在它們上面添加了一些類。

  咱們也能夠看到咱們本身添加的兩個消息控制器。

  最後,注意GreetingsWidget實例的<div class =「oe_petstore_greetings」>元素位於表明HomePage實例的<div class =「oe_petstore_homepage」>中,這是由於咱們追加了該元素。

  Widget的父類和子類

  在上一部分中,咱們使用如下語法實例化了一個小部件:

new local.GreetingsWidget(this);  //括號內對象是指greetingswidget實例化後歸誰全部。

  第一個參數是 this,在這種狀況下是一個HomePage實例。這告訴小部件被建立,其餘小部件是其父項。

  正如咱們所看到的,小部件一般由另外一個小部件插入到DOM中,並在其餘小部件的根元素內插入。這意味着大多數小部件是另外一個小部件的「部分」,並表明它存在。咱們將容器稱爲父項,並將包含的小部件稱爲子項。

  因爲技術和概念上的多重緣由,小部件有必要知道誰是其父類以及誰是子類。

  getParent() 能夠用來獲取小部件的父級:

  

local.GreetingsWidget = instance.Widget.extend({
    start: function() {
        console.log(this.getParent().$el );
        // will print "div.oe_petstore_homepage" in the console
    },
});

  getChildren() 能夠用來獲取其子女的名單:

local.HomePage = instance.Widget.extend({
    start: function() {
        var greeting = new local.GreetingsWidget(this);
        greeting.appendTo(this.$el);
        console.log(this.getChildren()[0].$el);
        // will print "div.oe_petstore_greetings" in the console
    },
});

   當重寫小部件的init()方法時,將父項傳遞給this._super()調用是很是重要的,不然關係將沒法正確設置:

local.GreetingsWidget = instance.Widget.extend({
    init: function(parent, name) {
        this._super(parent);
        this.name = name;
    },
});

  最後,若是小部件沒有父項(例如,由於它是應用程序的根小部件),則能夠將null做爲父項提供:

new local.GreetingsWidget(null);

  銷燬Widget

  若是您能夠向用戶顯示內容,則應該也能夠將其刪除。這是經過destroy()方法完成的:

greeting.destroy();

  當一個小部件被銷燬時,它將首先對其全部子項調用destroy()。而後它從DOM中刪除本身。若是你已經在init()或start()中設置了永久結構,必須明確清除它們(由於垃圾回收器不會處理它們),你能夠重寫destroy()。

  危險

  當覆蓋destroy()時,必須始終調用_super(),不然即便沒有顯示錯誤,小部件及其子項也沒有正確清理,從而可能會發生內存泄漏和「意想不到的事件」。

  QWeb模板引擎

  在上一節中,咱們經過直接操做(並添加)DOM來將內容添加到咱們的小部件:

this.$el.append("<div>Hello dear Odoo user!</div>");

  這容許生成和顯示任何類型的內容,但在生成大量DOM時會很難處理(大量重複,引用問題......)。

  與許多其餘環境同樣,Odoo的解決方案是使用模板引擎。 Odoo的模板引擎被稱爲QWeb。

   QWeb是一種基於XML的模板語言,與Genshi,Thymeleaf或Facelets相似。它具備如下特色:

  • 它在JavaScript中徹底實現並在瀏覽器中呈現;
  • 每一個模板文件(XML文件)都包含多個模板;
  • 它在Odoo Web的Widget()中有特殊的支持,雖然它能夠在Odoo的Web客戶端以外使用(而且能夠在不依賴於QWeb的狀況下使用Widget())。

  注意

  使用QWeb代替現有的JavaScript模板引擎的原理是預先存在的(第三方)模板的可擴展性,就像Odoo視圖同樣。

  大多數JavaScript模板引擎是基於文本的,這排除了容易的結構可擴展性,其中基於XML的模板引擎能夠經過使用例如通用數據庫XPath或CSS以及樹型變動DSL(甚至只是XSLT)。這種靈活性和可擴展性是Odoo的核心特徵,丟失它被認爲是不可接受的。

  使用QWeb

  首先讓咱們在幾乎空白的地方定義一個簡單的QWeb模板,在如下文件進行操做:

  oepetstore/static/src/xml/petstore.xml

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve"> <t t-name="HomePageTemplate">
        <div style="background-color: red;">This is some simple HTML</div>
    </t>
</templates>
  如今咱們能夠在HomePage小部件中使用這個模板。使用頁面頂部定義的QWeb加載器變量,咱們能夠調用XML文件中定義的模板:
local.HomePage = instance.Widget.extend({
    start: function() {
        this.$el.append(QWeb.render("HomePageTemplate"));
    },
});

  QWeb.render()查找指定的模板,將其呈現爲一個字符串並返回結果。

  可是,由於Widget()對QWeb有特殊的集成,因此模板能夠經過它的模板屬性直接設置在Widget上:

local.HomePage = instance.Widget.extend({
    template: "HomePageTemplate", 
    start: function() {
        ...
    },
});

  儘管結果看起來類似,但這些用法之間有兩點區別:

  • 在第二個版本中,模板在調用start()以前就被渲染了;
  • 在第一個版本中,模板的內容被添加到小部件的根元素,而在第二個版本中,模板的根元素被直接設置爲小部件的根元素。這就是爲何「greetings」子窗口小部件也會出現紅色背景。

  警告

   模板應該有一個非t根元素,特別是若是它們被設置爲一個小部件的模板。若是有多個「根元素」,結果是未定義的(一般只有第一個根元素將被使用,其餘元素將被忽略)。

   QWeb上下文

  QWeb模板能夠被賦予數據而且能夠包含基本的顯示邏輯。

  對於顯式調用QWeb.render(),模板數據做爲第二個參數傳遞:

QWeb.render("HomePageTemplate", {name: "Klaus"});

  將模板修改成:

<t t-name="HomePageTemplate">
    <div>Hello <t t-esc="name"/></div>
</t>

  最終結果爲:

<div>Hello Klaus</div>

  當使用Widget()的集成時,不可能爲模板提供額外的數據。該模板將被賦予一個單一的窗口小部件上下文變量,引用在start()被調用以前被渲染的窗口小部件(窗口小部件的狀態基本上是由init()設置的):

<t t-name="HomePageTemplate">
    <div>Hello <t t-esc="widget.name"/></div>
</t>
local.HomePage = instance.Widget.extend({
    template: "HomePageTemplate",
    init: function(parent) {
        this._super(parent);
        this.name = "Mordecai";
    },
    start: function() {
    },
});

  結果爲:

<div>Hello Mordecai</div>

  模板聲明

   咱們已經看到了如何渲染QWeb模板,如今讓咱們看看模板自己的語法。

  QWeb模板由常規XML和QWeb指令組成。 QWeb指令聲明瞭以t-開頭的XML屬性。

  最基本的指令是t-name,用於在模板文件中聲明新模板:

<templates>
    <t t-name="HomePageTemplate">
        <div>This is some simple HTML</div>
    </t>
</templates>

  t-name採用被定義模板的名稱,並聲明可使用QWeb.render()來調用它。它只能在模板文件的頂層使用。

  Escaping(文本輸出)

  t-esc指令可用於輸出文本:

<div>Hello <t t-esc="name"/></div>

  它須要一個通過評估的Javascript表達式,而後表達式的結果被HTML轉義並插入到文檔中。因爲它是一個表達式,所以能夠像上面那樣僅提供一個變量名稱,或者像計算這樣的更復雜的表達式:

<div><t t-esc="3+5"/></div>

  或方法調用:

<div><t t-esc="name.toUpperCase()"/></div>

  輸出HTML

  要在呈現的頁面中注入HTML,請使用t-raw。像t-esc同樣,它以一個任意的Javascript表達式做爲參數,但它不執行HTML轉義步驟。

<div><t t-raw="name.link('http://www.baidu.com')"/></div>  <!-- 產生一個超連接,指向百度-->

   危險

  t-raw不得用於用戶提供的任何可能包含非轉義內容的數據,由於這會致使跨站腳本漏洞。

  條件語句

  QWeb可使用t-if的條件塊。該指令採用任意表達式,若是表達式爲falsy(false,null,0或空字符串),則整個塊將被抑制,不然將顯示該表達式。

<div>
    <t t-if="true == true">
        true is true
    </t>
    <t t-if="true == false">
        true is not true
    </t>
</div>

  注意

  QWeb沒有「else」結構,若是原始條件反轉,則使用第二個t。若是它是複雜或昂貴的表達式,您可能須要將條件存儲在局部變量中。

  迭代

  要在列表上迭代,請使用t-foreach和t-as。 t-foreach須要一個表達式返回一個列表來迭代t - 由於在迭代過程當中須要一個變量名來綁定到每一個項目。

<div>
    <t t-foreach="names" t-as="name">
        <div>
            Hello <t t-esc="name"/>
        </div>
    </t>
</div>

  注意

  t-foreach也能夠用於數字和對象(字典)。

  定義屬性

  QWeb提供了兩個相關的指令來定義計算屬性:t-att-name和t-attf-name。不管哪一種狀況,name都是要建立的屬性的名稱(例如t-att-id在渲染後定義屬性id)。

  t-att-接受一個javascript表達式,其結果被設置爲屬性的值,若是計算該屬性的全部值,則它是很是有用的:

<div>
    Input your name:
    <input type="text" t-att-value="defaultName"/>
</div>

  t-attf-採用格式字符串。格式字符串是帶有插值塊的文本文本,插值塊是{{和}}之間的javascript表達式,它將被表達式的結果替換。對於部分文字和部分計算的屬性(如類),這是最有用的:

<div t-attf-class="container {{ left ? 'text-left' : '' }} {{ extra_class }}">
    insert content here
</div>

  調用其餘模板

  模板能夠拆分紅子模板(爲了簡單,可維護性,可重用性或避免過多的標記嵌套)。

  這是經過使用t-call指令完成的,該指令採用要呈現的模板的名稱:

<t t-name="A">
    <div class="i-am-a">
        <t t-call="B"/>
    </div>
</t>
<t t-name="B">
    <div class="i-am-b"/>
</t>

  渲染A模板將致使:

<div class="i-am-a">
    <div class="i-am-b"/>
</div>

  子模板繼承其調用者的渲染上下文。

 

  瞭解關於QWeb的更多信息

  練習:在Widgets中使用QWeb

  在Widgets建立一個構件除了parent:product_names和color以外還有兩個參數的構件。

  • product_names應該是一個字符串數組,每一個字符串都是一個產品的名稱 顏色是包含CSS顏色格式的顏色的字符串(即:#000000表示黑色)。
  • 小部件應該將給定的產品名稱一個顯示在另外一個下面,每一個顯示在一個單獨的框中,背景顏色爲顏色值和邊框。
  • 你應該使用QWeb來呈現HTML。任何須要的CSS應該在oepetstore / static / src / css / petstore.css中。 在HomePage中使用小部件,並有六種產品。
odoo.oepetstore = function(instance, local) {
    var _t = instance.web._t,
        _lt = instance.web._lt;
    var QWeb = instance.web.qweb;

    local.HomePage = instance.Widget.extend({
        start: function() {
            var products = new local.ProductsWidget(
                this, ["cpu", "mouse", "keyboard", "graphic card", "screen"], "#00FF00");
            products.appendTo(this.$el);
        },
    });

    local.ProductsWidget = instance.Widget.extend({
        template: "ProductsWidget",
        init: function(parent, products, color) {
            this._super(parent);
            this.products = products;
            this.color = color;
        },
    });

    instance.web.client_actions.add(
        'petstore.homepage', 'instance.oepetstore.HomePage');
}

  

  小工具助手

  小部件的jQuery選擇器

  在窗口小部件中選擇DOM元素能夠經過調用窗口小部件的DOM根目錄上的find()方法來執行:

this.$el.find("input.my_input")...

  可是因爲這是一種常見的操做,Widget()經過$()方法提供了一個等效的快捷方式:

local.MyWidget = instance.Widget.extend({
    start: function() {
        this.$("input.my_input")...
    },
});

  警告

  全局jQuery函數$()應該永遠不會被使用(不是this.$()),除非它是絕對必要的:對一個小部件的根進行選擇的範圍是小部件,對本地來講是本地的,可是使用$()的選擇對於頁面/應用程序是全局的,而且能夠匹配部分其餘小部件和視圖,致使奇怪或危險的反作用。因爲小部件一般只應用於其擁有的DOM部分,所以沒有全局選擇的緣由。

 

  更容易的DOM事件綁定

  咱們之前使用常規jQuery事件處理程序(例如,.click()或.change())在窗口小部件元素上綁定了DOM事件:

local.MyWidget = instance.Widget.extend({
    start: function() {
        var self = this;
        this.$(".my_button").click(function() {
            self.button_clicked();
        });
    },
    button_clicked: function() {
        ..
    },
});

   雖然這有效,但它有一些問題:

  • 它比較冗長
  • 它不支持在運行時替換小部件的根元素,由於綁定僅在start()運行時執行(在小部件初始化期間)
  • 它須要處理這個綁定問題

  小部件所以提供了經過事件綁定DOM事件的捷徑:

local.MyWidget = instance.Widget.extend({
    events: {
        "click .my_button": "button_clicked",
    },
    button_clicked: function() {
        ..
    }
});

  event 是事件觸發時調用的函數或方法的對象(映射):

  關鍵是一個事件名稱,可能使用CSS選擇器進行優化,在這種狀況下,只有當事件發生在選定的子元素上時,函數或方法纔會運行:點擊將處理小部件內的全部點擊,但單擊.my_button將只處理點擊含有my_button類的元素。

  該值是觸發事件時要執行的操做。

  它也能夠這樣描述:

events: {
    'click': function (e) { /* code here */ }
}

  或對象上方法的名稱(請參見上面的示例)。

  不管哪一種狀況,這都是小部件實例,而且處理程序被賦予一個參數,即事件的jQuery事件對象。

  小部件事件和屬性

  事件

  小部件提供了一個事件系統(與上面描述的DOM / jQuery事件系統分開):一個小部件能夠觸發自身的事件,其餘小部件(或其自己)能夠綁定本身並監聽這些事件:

local.ConfirmWidget = instance.Widget.extend({
    events: {
        'click button.ok_button': function () {
            this.trigger('user_chose', true);
        },
        'click button.cancel_button': function () {
            this.trigger('user_chose', false);
        }
    },
    start: function() {
        this.$el.append("<div>Are you sure you want to perform this action?</div>" +
            "<button class='ok_button'>Ok</button>" +
            "<button class='cancel_button'>Cancel</button>");
    },
});

  trigger()將觸發事件的名稱做爲其第一個(必需)參數,任何其餘參數都視爲事件數據並直接傳遞給偵聽器。

  而後,咱們能夠設置一個父事件來實例化咱們的通用小部件,並使用on()來監聽user_chose事件:

local.HomePage = instance.Widget.extend({
    start: function() {
        var widget = new local.ConfirmWidget(this);
        widget.on("user_chose", this, this.user_chose);
        widget.appendTo(this.$el);
    },
    user_chose: function(confirm) {
        if (confirm) {
            console.log("The user agreed to continue");
        } else {
            console.log("The user refused to continue");
        }
    },
});

  on()綁定一個函數,當event_name標識的事件發生時被調用。 func參數是要調用的函數,object是該函數與方法相關的對象。綁定函數將被調用trigger()(若是有的話)的附加參數。例:

start: function() {
    var widget = ...
    widget.on("my_event", this, this.my_event_triggered);
    widget.trigger("my_event", 1, 2, 3);
},
my_event_triggered: function(a, b, c) {
    console.log(a, b, c);
    // will print "1 2 3"
}

  提示:

  觸發其餘小部件上的事件一般是一個壞主意。該規則的主要例外是odoo.web.bus,它專門用於廣播任何小部件可能對整個Odoo Web應用程序感興趣的平臺。

相關文章
相關標籤/搜索