zoukankan      html  css  js  c++  java
  • 【原创】Odoo开发文档学习之:构建接口扩展(Building Interface Extensions)(边Google翻译边学习)

      构建接口扩展(Building Interface Extensions)

      本指南是关于为Odoo的web客户创建模块。

      要创建有Odoo的网站,请参见建立网站;要添加业务功能或扩展Odoo的现有业务系统,请参见构建模块

    警告:
    该指南需要以下知识:
    Javascript 、jQuery、Underscore.js
    同时也需要安装 Odoo 和 Git。

      一个简单的模型

      让我们从一个简单的Odoo模块开始,它包含基本的web组件配置,并让我们测试web框架。
      示例模块可以在线下载,可以使用以下命令下载:

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

      这将在您执行命令的地方创建一个petstore文件夹。然后需要将该文件夹添加到Odoo的addons路径中,创建一个新的数据库并安装oepetstore模块。

      如果您浏览petstore文件夹,您应该看到以下内容:

    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)中。

      在Odoo模块的“web”端中使用的文件必须放置在静态文件夹中,这样它们就可以在web浏览器中使用,而浏览器之外的文件也不能被浏览器获取。src/css、src/js和src/xml子文件夹是常规的,并不是绝对必要的。

      

    oepetstore/static/css/petstore.css

      目前为空,将为宠物店(pet store)内容保留CSS。

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

  • 相关阅读:
    C# 打印PPT幻灯片
    Java 创建/识别条形码、二维码
    Java 添加Word文本框
    Java 复制PPT幻灯片
    C# 读取Word内容控件
    Java 操作Word书签(三):用文本、图片、表格替换书签
    Java 操作Word书签(二):添加文本、图片、表格到书签内容
    C#/Java 动态生成电子发票
    C# 复制Excel单元格格式
    Java 操作Word书签(一):添加、删除、读取书签
  • 原文地址:https://www.cnblogs.com/KKSoft/p/8445591.html
Copyright © 2011-2022 走看看