Odoo QWeb

1.web 模块

注意,OpenERP 模块中 web 部分用到的所有文件必须被放置在模块内的 static 文件夹里。这是强制性的,出于安全考虑。

事实上,我们创建的文件夹 CSS,JS 和 XML,仅仅是一个习惯。

static文件夹

oepetstore/static/css/petstore.css 是我们的 CSS 文件。

oepetstore/static/xml/petstore.xml 是一个 XML 文件,将包含我们 QWeb 的模板。

oepetstore/static/js/petstore.js包含应用程序的JavaScript 代码。

像 OpenERP 的 XML 文件包含了视图或数据一样,必须在__openerp__.py文件内标明这些文件。下面是我们增加的行,它告诉 web client 必须记载这些文件:

'js': ['static/src/js/*.js'],

'css': ['static/src/css/*.css'],

'qweb': ['static/src/xml/*.xml'],

OpenERP内,默认会把所有的 JavaScript 文件连接为一个文件。然后,我们执行一个叫 minification的操作。

minification 将移除文件中的所有的注释、空格和换行符。最后,发送这个文件给用户浏览器。

但这么做的缺点,是无法调试应用程序。为了避免这种副作用,仍然能够调试的解决办法是:在 OpenERP 的 URL 后面添加一个参数 ?debug .

添加后的URL:http://localhost:8069/?debug

当您使用带 debug 参数的 URL,应用程序将不会执行串联所有 minification的 JavaScript 文件这个过程。应用程序也将需要更多的时间来加载,但你能进

行调试开发了。

在前面的章节中,我们解释了 JavaScript 缺少命名空间机制,来分割在不同的 JavaScript 文件中声明的变量。并且我们提出了模块模式这个简单方法。

模块模式(如 app.js文件):

(function() {

app = {};

function main() {

console.log("launch application");

};

app.main = main;

})();

在 OpenERP 的 web 框架内,有个类似于模块模式的等价物,集成了该框架的其余部分。请注意,OpenERP 的 Web 模块与其他 OpenERP 的 addon 模块概念上是不同的,一个 addon 模块是一个包含很多文件的文件夹,web 模块仅仅是一个有命名空间概念的 JavaScript。

1)模块

oepetstore/static/js/petstore.js 声明了这样的模块:

openerp.oepetstore=function(instance){

instance.oepetstore={};

instance.oepetstore.XXX=......;

}

在OpenERP的Web框架内,通过声明一个函数来声明一个 JavaScript 模块,并把这个函数放在全局变量openerp的属性内.这个属性名称必须和OpenERP addon 模块名称一致 (这 addon 模块名为 oepetstore,我应把函数赋值给openerp.oepetstore属性。如果换成openerp.petstore属性,则无法正常运行)。当 Web 客户端转载这个 addon 模块时,该函数将被调用。将传入一个名为instance的参数,这个参数代表当前 OpenERP 的 Web 客户端实例, 包含了所有相关当前会话数据,以及所有 Web 模块的变量。

在 instance 对象内创建与 addon 模块名称一致的新命名空间是个惯例。这就是为什么我们在 instance.oepetstore 设置一个空 dictionary。这个 dictionary就是命名空间,用来声明我们模块内自己使用的所有类和变量。

2)类

JavaScript 不像其他面向对象编程语言那样有类机制。更确切地说,它提供了面向对象编程语言元素,但你必须自己定义,自己选择如何做。

OpenERP Web 框架提供工具来简化这个过程,让程序员以类似其他编程语言,如 Java 的方式编码。

定义一个新类,你需要从 instance.web.Class 类继承。语法如下:

instance.oepetstore.MyClass = instance.web.Class.extend({

say_hello: function() {

console.log("hello");

},

});

类的实例化:

var my_object = new instance.oepetstore.MyClass();

my_object.say_hello();

类可以有一个构造函数,它方法名为 init()。你可以像大多数开发语言那样,传递参数给构造器:

instance.oepetstore.MyClass = instance.web.Class.extend({

init: function(name) {

this.name = name;

},

say_hello: function() {

console.log("hello", this.name);

},

});

类的继承

类可以被继承

instance.oepetstore.MySpanishClass = instance.oepetstore.MyClass.extend({

say_hello: function() {

this._super();

console.log("hola", this.name);

},

});

var my_object = new instance.oepetstore.MySpanishClass("Nicolas");

my_object.say_hello();

this._super()不是常用的类方法

3)Widgets Basics(基础部件)

OpenERP中Widget部件,是一个通用组件,专门用来向用户显示内容。

oepetstore实例中的petstore.js内容:

openerp.oepetstore=function(instance){

var _t=instance.web._t,

_lt=instance.web._lt;

var QWeb=instance.web.qweb;

 

instance.oepetstore={};

instance.oepetstore.HomePage=instance.web.Widget.extend({

start:function(){

console.log("pet store home page loaded");

},

});

 

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

}

最后一行代码,把这个部件注册为客户端的action。当我们点击‣Pet Store ‣ Pet Store ‣ Home Page菜单项时,客户端action让部件显示出来。

HomePage 部件有一个 start() 方法。 在部件初始化后,这方法被自动调用。它已接接受指令去显示其内容。我们将用它向用户显示一些内容。要做到这一点,我们使用所有部件都有的 $el 属性。该属性是一个 jQuery对象, 表示部件对应的 HTML 标签的根标签。部件包含了多个 HTML 标签,这些 HTM 标签有一个统一的根标签。默认情况下,部件都有一个空的根标签:一个

一个

HTML 标签在没有具体内容的时候,是不可见的。这也解释了为什么显示 instance.oepetstore.HomePage 时, 是个空白区域, 它根本没有任何内容。要想显示些内容,我们用 jQuery 该对象上的一些简单方法,在根标签中添加一些 HTML 标签:

instance.oepetstore.HomePage = instance.web.Widget.extend({

start: function() {

this.$el.append("

Hello dear OpenERP user!
");

},

});

常用方法:

appendTo()方法

var pettoys = new instance.oepetstore.PetToysList(this);//实例化PetToysList()部件

pettoys.appendTo(this.$(".oe_petstore_homepage_left"));//把pettoys添加到当前部件的class=.oe_petstore_homepage_left的标签中

注意:new instance.oepetstore.PetToysList(this),其中参数this,代表调用此部件的实例,表示部件的隶属关系。

addClass( )方法

this.$el.addClass("oe_petstore_homepage");//添加样式

getParent()方法

用于获取父部件

getChildren() 方法

用来获取子部件列表:this.getChildren()[0].$el

当你在部件中重载init()时,必须以父部件作为第一参数传入,并调用传入给this._super(),例如:

instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({

init: function(parent, name) {

this._super(parent);

this.name = name;

},

});

最后,如果一个部件没有父部件(即:在你的应用实例化时是第一个部件),传入 null 参数:

new instance.oepetstore.GreetingsWidget(null);

销毁部件:

destroy()方法:

当一个部件被销毁,它会先调用所有子部件的destroy()。然后,它在DOM中清楚自己。通过部件隶属关系的递归调用,避免了内存泄漏,这对容易产生内

存泄露的大型JavaScript应用程序来说是非常有用的。

 

2.Qweb

在 OpenERP 中,使用Qweb模板引擎,专门用于Web 客户端开发。

Qweb 是一种基于XML的模板语言,类似Genshi,Thymeleaf 或 Facelets。

有如下几个特点:

① 在浏览器中完全用 JavaScript 执行。

② 每个模板文件(XML 文件)包含了多个模板,而其他模板引擎通常做法是,模板文件和模板之间 1:1 的关系。

③ 在 OpenERP 的 Web 的 instance.web.Widget 特别支持了 QWeb。虽然QWeb 可以用于 OpenERP 的 Web 客户端以外的地方(同时instance.web.Widget 也可以不依赖于 Qweb)。

之所以没用用其他 JavaScript 模板引擎,而是选择了 QWeb, 是因为 QWeb的扩展机制与 OpenERP 的视图继承机制很相似。就像 OpenERP 的视图一样,QWeb 模版也是个 XML 树结构,因此很容易在模版执行 XPath 或 DOM 操作。

在部件内使用 QWeb

首先,在文件 oepetstore/static/src/xml/petstore.xml 里我们定义一个简单的QWeb 模板。

 
 

This is some simple HTML

 
现在,修改HomePage类: 

var QWeb = instance.web.qweb;

我们建议在所有的 OpenERP web 模块复制粘贴这行代码。这个对象提供访问被Web客户端加载的所有模版文件中的模版的功能。下面是我们使用XML模板文件定义的模版的例子:

instance.oepetstore.HomePage = instance.web.Widget.extend({

start: function() {

this.$el.append(QWeb.render("HomePageTemplate"));

},

});

解释:QWeb.render()方法用来渲染指定标识名称的具体模版,第一个参数就是模版标识名称。

另外一个常用地方是,在部件内集成 Qweb:

instance.oepetstore.HomePage = instance.web.Widget.extend({

template: "HomePageTemplate",

start: function() {

...

},

});

当你在部件内设置类属性 template 时,部件就知道它需要调用 QWeb.render()来呈现该模板。

请注意这两个语法是有区别的。当在部件内集成 Qweb 时, QWeb.render() 调用在部件调用 start() 之前发生,并用模版的根标签替换了部件的默认根标签。这会导致不同的结果,所以你应该记住它。

Qweb 上下文(Context)

像所有的模板引擎一样, Qweb 模板可以包含操纵传递给模板的数据的代码。

1)使用QWeb.render()的第二个参数传递数据给模版:

Hello 

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

输出结果: 

Hello Nicolas

2)当部件整合 QWeb 时,就不能直接传入数据了。 替换办法是,模板有一个唯一变量 widget ,这个变量引用了当前部件:

Hello 

instance.oepetstore.HomePage = instance.web.Widget.extend({

template: "HomePageTemplate",

init: function(parent) {

this._super(parent);

this.name = "Nicolas";

},

start: function() {

},

});

输出结果: 

Hello Nicolas

模版声明

Qweb 的语法:

Qweb 指令使用前缀 t- 的 XML 属性来声明新模板,我们在 XML 文件的根元素