【原创】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应用程序感兴趣的平台。

相关文章
相关标签/搜索