-----------
更新日期
15:17 2016-02-16 星期二
-----------
* 用到的js库
我们可以打开 addons/web/views/webclient_template.xml
看到如下:
<template id="web.assets_common">
<script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script>
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
<script type="text/javascript" src="/web/static/lib/datejs/globalization/en-US.js"></script>
<script type="text/javascript" src="/web/static/lib/spinjs/spin.js"></script>
<!-- jQuery stuff -->
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.blockUI/jquery.blockUI.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.hotkeys/jquery.hotkeys.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.placeholder/jquery.placeholder.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.timeago/jquery.timeago.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.form/jquery.form.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
<script type="text/javascript" src="/web/static/src/js/tour.js"></script>
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
</template>
# es5-shim 给傻逼浏览器做兼容性,使得傻逼浏览器可以支持一些 es5 的 api
# spinjs ajax异步时,等待出现一个轮的图片
# datejs 日期处理js
# jQuery库,这是经典库了 http://t.mb5u.com/jquery/ 1.8.3
# jquery.blockUI 提示窗口
# jquery.hotkeys 键盘js处理
# jquery.placeholder 实现文本框显示描述文字
# jquery.timeago 时间格式
# jquery.form 表单处理
# jquery.ba-bbq
# underscore库,弥补了jQuery没有实现的地方 文档http://www.css88.com/doc/underscore1.6.0/
所有的功能都封装在名为"_"命名空间内
常用_.each()来代替javascript中的循环
_.range()产生一个范围的数列
function sumTotal(){
var x=0;
_.each(_.range(1,101),function(i){
x+=i;
});
console.log("Result",x);
}
*jQuery 简单操作
#选择器
@ $("input") 选择特定的HTML元素
@ $("#content") 选择指定的id的HTML元素
@ $(".title") 选择明确的css样式类的所有元素
@ $("span.title") 组合选择元素,可以更精确选择元素
# 事件
@ $("button").click(function){
console.log("someone clicked on the button")
}); // 按钮上的单点事件
# 修改DOM
@ $(".main_content").html('<div style="color:white">Hello world!' </div>) <!--替换内容-->
@ $(".main_content").append('<div style="color:white">Hello world again!' </div>) <!--追加内容到后面-->
@ $(".main_content").prepend('<div style="color:white">Hello world front!' </div>) <!--追加内容到前面-->
放文本用 text()
$(".main_content").text('The <div> element will appear as-is in the browser.');
# 异步调用
服务端:
@app.route('/service_plus', methods=["POST"])
def service_plus():
data = flask.request.json
a = data["a"]
b = data["b"]
delay = data.get("delay", 0)
time.sleep(delay)
return flask.jsonify(**{
"addition": a + b,
})
客户端:
$.ajax("/service_plus", {
type: "POST",
dataType: "json",
data: JSON.stringify({
"a": 3,
"b": 5,
}),
contentType: "application/json",
}).then(function(a) {
console.log("3+5=", a.addition);
});
说明:
@ JSON.stringify 把字典转换为json
@ flask.jsonify 返回json串
在所有的处理异步操作中,总是返回一个deferred
function func1() {
var def1 = $.ajax(...); // A first call to the server.
var def2 = $.ajax(...); // A second call.
var def3 = $.when(def1, def2); // We multiplex all that.
var def4 = def3.then(...); // Some more complexity: we chain it.
// Now we don't forget to return a deferred that represents the complete
operation.
return def4;
};
function func2() {
var def = func1(); // We want to call func1().
// Now if I need to know when func1() has finished all its operations I h
ave a deferred that represents that.
var def2 = def.then(...);
// And finally we don't forget to return a deferred because func2() is, b
y transitivity, a function
// that performs an asynchronous call. So it should return a deferred too.
return def2;
};
*模块定义
(function() {
app = {};
function main() {
console.log("launch application");
};
app.main = main;
})();
采用匿名函数封装
* 例子
function openerp_picking_widgets(instance){
var module = instance.stock;
var _t = instance.web._t;
var _lt = instance.web._lt;
var QWeb = instance.web.qweb;
// This widget makes sure that the scaling is disabled on mobile devices.
// Widgets that want to display fullscreen on mobile phone need to extend this
// widget.
module.MobileWidget = instance.web.Widget.extend({
start: function(){
if(!$('#oe-mobilewidget-viewport').length){
$('head').append('<meta id="oe-mobilewidget-viewport" name="viewport" content="initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">');
}
return this._super();
},
destroy: function(){
$('#oe-mobilewidget-viewport').remove();
return this._super();
},
});
#var _t = instance.web._t; 翻译
* 采用异步 做 (a+b)*(c+d) 这个功能 ,这是例子,主要参考模式思想
(function(){
app = {};
function main(){
$("button").click(function(){
plusmultplus(1,2,3,4).then(function(result){
console.log("(1+2)*(3+4)=",result.multiplication);
});
});
}
app.main = main;
function plusmultplus(a,b,c,d){
var def1 = $.ajax("/service_plus",{
type:"POST",
dataType:"json",
data:JSON.stringify({
"a":a,
"b":b,
}),
contentType:"application/json",
});
var def2 = $.ajax("/service_plus",{
type:"POST",
dataType:"json",
data:JSON.stringify({
"c":c,
"d":d,
}),
contentType:"application/json",
});
return $.when(def1,def2).then(function(result1,result2){
return $.ajax("/service_mult",{
type:"POST",
dataType:"json",
data:JSON.stringify({
"a":result1[0].addition,
"b":result2[0].addition,
}),
contentType:"application/json",
});
});
}
})();
--------------------------------
@app.route('/service_plus', methods=["POST"])
def service_plus():
data = flask.request.json
a = data["a"]
b = data["b"]
delay = data.get("delay", 0)
time.sleep(delay)
return flask.jsonify(**{
"addition": a + b,
})
@app.route('/service_mult', methods=["POST"])
def service_mult():
data = flask.request.json
a = data["a"]
b = data["b"]
delay = data.get("delay", 0)
time.sleep(delay)
return flask.jsonify(**{
"multiplication": a * b,
})
* Web Framework 构造图形化的javascript 应用
# 基础框架js所在位置 addons/web/static/src/js/openerpframework
#js 在odoo中的运用
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');
}
@ odoo运行时会把所有的javascript文件连接为一个文件,然后压缩,若要调试,必须采用debug模式
@ 上面创建了, oepetstore 模块,且做为属性放在全局变量openerp中;模块名和addon的模块名要一
致,否则不能运行
@ 当载入addon这个模块时,该js模块调用,传入instance参数,这个参数代表当前 OpenERP 的 Web 客户端实例,
包含了所有相关当前会话数据, 以及所有 Web 模块的变量
@ instance.oepetstore = {}; 这是命名空间,用来声明我们模块内自己使用的所有类和变量
# js在odoo定义一个新类
instance.oepetstore.MyClass = instance.web.Class.extend({
say_hello: function(){
console.log("hello");
}
});
@ 调用instance.web.Class.extend() 传入一个dictionary 参数
@ 在方法内,用this访问属性值
instance.oepetstore.MyClass = instance.web.Class.extend({
say_hello: function() {
console.log("hello", this.name);
},
});
var my_object = new instance.oepetstore.MyClass();
my_object.name = "Nicolas";
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);
},
});
var my_object = new instance.oepetstore.MyClass("Nicolas");
my_object.say_hello();
@ 重载方法时,使用 this._super()调用原来的方法
instance.oepetstore.MySpanishClass = instance.oepetstore.MyClass.extend({
say_hello: function() {
this._super();
console.log("translation in Spanish: hola", this.name);
},
});
var my_object = new instance.oepetstore.MySpanishClass("Nicolas");
my_object.say_hello();
* Widgets
# 第一个部件
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
@ start 这个方法部件初始化会自动调用
@ 改造一下用上jQuery 的$el
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
this.$el.append("<div>Hello dear OpenERP user!</div>")
},
});
# 实例化部件
instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
start: function() {
this.$el.addClass("oe_petstore_greetings");
this.$el.append("<div>We are so happy to see you again in this menu!</div>");
},
});
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
this.$el.addClass("oe_petstore_homepage");
this.$el.append("<div>Hello dear OpenERP user!</div>");
var greeting = new instance.oepetstore.GreetingsWidget(this);
greeting.appendTo(this.$el);
},
});
显示结果:
<div class="oe_petstore_homepage">
<div>Hello dear OpenERP user!</div>
<div class="oe_petstore_greetings">
<div>We are so happy to see you again in this menu!</div>
</div>
</div>
@ new instance.oepetstore.GreetingsWidget(this); 实例化部件
this 参数,在这里代表 HomePage实例,部件有父子关系
instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
start: function() {
console.log(this.getParent().$el );
// will print "div.oe_petstore_homepage" in the console
},
});
----------
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
var greeting = new instance.oepetstore.GreetingsWidget(this);
greeting.appendTo(this.$el);
console.log(this.getChildren()[0].$el);
// will print "div.oe_petstore_greetings" in the console
},
});
@ 当重载部件的init()时,必须以父部件作为第一参数传入,并调用传入给 this._super()
instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
init: function(parent, name) {
this._super(parent);
this.name = name;
},
});
当一个部件没有父部件时,实例化传null参数
# 销毁部件
greeting.destory()
# 事件
instance.oepetstore.ConfirmWidget = instance.web.Widget.extend({
start: function() {
var self = this;
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>");
this.$el.find("button.ok_button").click(function() {
self.trigger("user_choose", true);
});
this.$el.find("button.cancel_button").click(function() {
self.trigger("user_choose", false);
});
},
});
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
var widget = new instance.oepetstore.ConfirmWidget(this);
widget.on("user_choose", this, this.user_choose);
widget.appendTo(this.$el);
},
user_choose: function(confirm) {
if (confirm) {
console.log("The user agreed to continue");
} else {
console.log("The user refused to continue");
}
},
});
@ this 隐式传入到所有的函数,每个已声明的函数都有自己的 this 。
所以,当我们在一个函数内声明了另一个函数,这个新功能将有自己的 this ,
这和父函数 this 含义不同,采用了 var self = this; 保存
@ self.trigger("user_choose", true); 定义user_choose为名事件的触发器
Widget.trigger(event_name [, ...]) 方法的第一个参数是待触发的事件名,
也接受任何数量的其他参数。这些参数将被传递到所有的事件侦听器
@ widget.on("user_choose", this, this.user_choose); 监听user_choose 事件
Widget.on(event_name, object, func) 允许绑定一个事件 event_name 触发时调
用的函数 func 。 如果 func) 是个方法,则 object 是 func) 函数的引用关联
对象。 当 func) 被调用时, trigger() 的其他参数会传递给它
# 属性
和普通对象属性一样,但多了一个功能,会触发事件
start: function() {
this.widget = ...
this.widget.on("change:name", this, this.name_changed);
this.widget.set("name", "Nicolas");
},
name_changed: function() {
console.log("The new value of the property 'name' is", this.widget.get("name"));
}
@ Widget.set(name, value) 方法设置某个属性的值。如果该值改变(或以前没有
值) , 对象将触发一个事件 change:xxx : xxx 是属性名称。
Widget.get(name) 读取属性值。
# 部件辅助工具
选择器 this.$el.find("input.my_input") <=> this.$("input.my_input")
要分析jQuery事件和部件事件
简化jQuery事件写法
start: function() {
var self = this;
this.$(".my_button").click(function() {
self.button_clicked();
});
}
--------
events: {
"click .my_button": "button_clicked",
},
# 翻译
记得js源码有两行
var _t = instance.web._t,
_lt = instance.web._lt;
这是导入翻译功能
this.$el.text(_t("Hello dear user!"));
和对应python代码中 _() 翻译
_lt()返回一个函数
var text_func = _lt("Hello dear user!");
this.$el.text(text_func());
class openerp.Widget()
是所有可视化组件的基础类
# DOM Root
Widget() 得到DOM Root
openerp.Widget.el Root
openerp.Widget.$el jQuery打包
openerp.Widget.template 生成Root
openerp.Widget.tagName 生成元素 默认是div
openerp.Widget.id 生成Root的id属性
openerp.Widget.className
openerp.Widget.renderElement() 渲染是生成Root
# 使用widget
openerp.Widget.init(parent)
加元素
openerp.Widget.appendTo(element) 加到后面
openerp.Widget.prependTo(element) 加到前面
openerp.Widget.insertAfter(element) 插在元素的后面
openerp.Widget.insertBefore(element) 插在元素的前面
openerp.Widget.destory()清理
openerp.Widget.alive(deferred[, reject=false]) 状态操作
openerp.Widget.isDestroyed() 检测有没有销毁
# 访问DOM内容
openerp.Widget.$(selector)
this.$(selector)
this.$el.find(selector)
# 重设DOM Root
openerp.Widget.setElement(element)
element 可以是元素,也可以jQuery对象
# DOM事件处理
openerp.Widget.events
如
events:{
'click p.oe_some_class a':'some_method',
'change input':function(e){
e.stopPropagation()
}
}
openerp.Widget.delegateEvents() 代理绑定到DOM上面
openerp.Widget.undelegateEvents() 解绑
# 子类Widget
通过extend
var MyWidget = openerp.Widget.extend({
// QWeb template to use when rendering the object
template: "MyQWebTemplate",
events: {
// events binding example
'click .my-button': 'handle_click',
},
init: function(parent) {
this._super(parent);
// insert code to execute before rendering, for object
// initialization
},
start: function() {
var sup = this._super();
// post-rendering initialization code, at this point
// allows multiplexing deferred objects
return $.when(
// propagate asynchronous signal from parent class
sup,
// return own's asynchronous signal
this.rpc(/* … */))
}
});
// Create the instance
var my_widget = new MyWidget(this);
// Render and insert into DOM
my_widget.appendTo(".some-div");
my_widget.destroy();
# 开发指南
@ 尽量少用id ,万一要用id 要用 _.uniqueId() 生成
this.id=_.uniqueId()
@ 尽量少用普通css名字 如 content navigator
@ 尽量少用全局选择器 用 Widget.$el 或 Widget.$()
@ 所有的组件都要继承 Widget()
@ 要用QWeb进行HTML模板和渲染
* QWeb
instance.web.Widget 特别支持 QWeb
<?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>
前面js源码,有 var QWeb = instance.web.qweb; 这样可以用QWeb功能了
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
this.$el.append(QWeb.render("HomePageTemplate"));
},
});
@ 渲染了 HomePageTemplate 模板
@ 也可以采用集成方式
instance.oepetstore.HomePage = instance.web.Widget.extend({
template: "HomePageTemplate",
start: function() {
},
});
发生在start方法之前,会用模板的根标签替换部件的默认根标签
# 传递参数
<t t-name="HomePageTemplate">
<div>Hello <t t-esc="name"/></div>
</t>
------
QWeb.render("HomePageTemplate", {name: "Nicolas"});
当采用集成式,要用变量 widget
<t t-name="HomePageTemplate">
<div>Hello <t t-esc="widget.name"/></div>
</t>
--------
instance.oepetstore.HomePage = instance.web.Widget.extend({
template: "HomePageTemplate",
init: function(parent) {
this._super(parent);
this.name = "Nicolas";
},
start: function() {
},
});
# 模板基础 https://doc.odoo.com/trunk/web/qweb/
<templates>
<t t-name="HomePageTemplate">
<div>This is some simple HTML</div>
</t>
</templates>
@ <templates> 模板的根元素
前缀 t-
@ t-name 标识模板, QWeb.render() 可以调用指定模板
@ t-esc 在HTML中放置文本 会转义html标签
@ t-raw 保持原有内容输出
@ t-if 条件语句
<t t-if="true==true">
true is true
</t>
@ t-foreach t-as 循环
<t t-foreach="names" t-as="name">
<div>
Hello <t t-esc="name" />
</div>
</t>
@ t-att-xx 设置属性值
* 部件调用数据
class message_of_the_day(osv.osv):
_name = "message_of_the_day"
def my_method(self, cr, uid, context=None):
return {"hello": "world"}
_columns = {
'message': fields.text(string="Message"),
'color': fields.char(string="Color", size=20),
}
-----------
instance.oepetstore.HomePage = instance.web.Widget.extend({
start: function() {
var self = this;
var model = new instance.web.Model("message_of_the_day");
model.call("my_method", [], {context: new instance.web.CompoundConte
xt()}).then(function(result) {
self.$el.append("<div>Hello " + result["hello"] + "</div>");
// will show "Hello world" to the user
});
},
});
#连接模型用 instance.web.Model
#采用model.call()来调用数据 call(name, args, kwargs) 是 Model 的方法
args 是对象方法的参数列表
kwargs 命名参数列表,这里只传了context
# 模型中的方法始终有一个参数 context , context 是一个包含多个key的dictonary
# CompoundContext 这个类用来传递用户上下文(语言,时区等..) 给服务器的
其构造函数的参数是任意数量的 dictionary
* 部件较好的例子
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({
template: "HomePage",
start: function() {
var pettoys = new instance.oepetstore.PetToysList(this);
pettoys.appendTo(this.$(".oe_petstore_homepage_left"));
var motd = new instance.oepetstore.MessageOfTheDay(this);
motd.appendTo(this.$(".oe_petstore_homepage_right"));
},
});
instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
instance.oepetstore.MessageOfTheDay = instance.web.Widget.extend({
template: "MessageofTheDay",
init: function() {
this._super.apply(this, arguments);
},
start: function() {
var self = this;
new instance.web.Model("message_of_the_day").query(["message"]).
first().then(function(result) {
self.$(".oe_mywidget_message_of_the_day").text(result.message);
});
},
});
instance.oepetstore.PetToysList = instance.web.Widget.extend({
template: "PetToysList",
start: function() {
var self = this;
new instance.web.Model("product.product").query(["name", "image"])
.filter([["categ_id.name", "=", "Pet Toys"]]).limit(5).all().
then(function(result) {
_.each(result, function(item) {
var $item = $(QWeb.render("PetToy", {item: item}));
self.$el.append($item);
});
});
},
});
}
----------------
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="HomePage">
<div class="oe_petstore_homepage">
<div class="oe_petstore_homepage_left"></div>
<div class="oe_petstore_homepage_right"></div>
</div>
</t>
<t t-name="MessageofTheDay">
<div class="oe_petstore_motd">
<p class="oe_mywidget_message_of_the_day"></p>
</div>
</t>
<t t-name="PetToysList">
<div class="oe_petstore_pettoyslist">
</div>
</t>
<t t-name="PetToy">
<div class="oe_petstore_pettoy">
<p><t t-esc="item.name"/></p>
<p><img t-att-src="'data:image/jpg;base64,'+item.image"/></p>
</div>
</t>
</templates>
.oe_petstore_homepage {
display: table;
}
.oe_petstore_homepage_left {
display: table-cell;
width : 300px;
}
.oe_petstore_homepage_right {
display: table-cell;
width : 300px;
}
.oe_petstore_motd {
margin: 5px;
padding: 5px;
border-radius: 3px;
background-color: #F0EEEE;
}
.oe_petstore_pettoyslist {
padding: 5px;
}
.oe_petstore_pettoy {
margin: 5px;
padding: 5px;
border-radius: 3px;
background-color: #F0EEEE;
}
====================
* RPC
采用异步的方式来调用
#高级API
访问对象方法用 openerp.Module
映射服务端对象用 call() 和 query()
var Users = new openerp.Model('res.users');
Users.call('change_password', ['oldpassword', 'newpassword'],
{context: some_context}).then(function (result) {
// do something with change_password result
});
query()是 search+read 在后端操作
Users.query(['name', 'login', 'user_email', 'signature'])
.filter([['active', '=', true], ['company_id', '=', main_company]])
.limit(15)
.all().then(function (users) {
// do work with users records
});
openerp.Model.call(method[, args][, kwargs])
method 是rpc的方法名
args 传入方法的参数
kwargs 关键词参数
openerp.Model.query(fields)
fields 字段列表
first() 取第一个记录
class openerp.web.Query(fields)类下方法
openerp.web.Query.all() 得到上面query() 集的所有
openerp.web.Query.first() 要第一条,没有就是null
openerp.web.Query.count() 得到的记录总数
openerp.web.Query.group_by(grouping...) 分组来列表
openerp.web.Query.context(ctx) 添加上下文
openerp.web.Query.filter(domain) 条件过滤domain表达式
opeenrp.web.Query.offset(offset) 设定起点
openerp.web.Query.limit(limit) 设定要返回的数量
openerp.web.Query.order_by(fields…) 记录排序
#聚合
some_query.group_by(['field1', 'field2']).then(function (groups) {
// do things with the fetched groups
})
openerp.web.QueryGroup.get(key)
得到key的值,key可以为:
@ grouped_on
@ value
@ length
@ aggregates
openerp.web.QueryGroup.query([fields...]) 等价于
openerp.web.Model.query()
openerp.web.QueryGroup.subgroups()
#低级API RPC访问python
opeenrp.session()
如:
openerp.session.rpc('/web/dataset/resequence', {
model: some_model,
ids: array_of_ids,
offset: 42
}).then(function (result) {
// resequence didn't error out
}, function () {
// an error occured during during call
});
========================
* Web 客户端
写测试用例
# 断言
ok(state[, message])
strictEqual(actual, expected[, message]) 相当于 ok(actual === expected, message))
notStrictEqual(actual, expected[, message]) 相当于 ok(actual !== expected, message))
deepEqual(actual, expected[, message])
notDeepEqual(actual, expected[, message])
throws(block[, expected][, message]) 抛出异常
equal(actual, expected[, message]) 宽松相等
notEqual(actual, expected[, message])
示例:
{
'name': "Demonstration of web/javascript tests",
'category': 'Hidden',
'depends': ['web'],
'js': ['static/src/js/demo.js'],
'test': ['static/test/demo.js'],
}
// src/js/demo.js
openerp.web_tests_demo = function (instance) {
instance.web_tests_demo = {
value_true: true,
SomeType: instance.web.Class.extend({
init: function (value) {
this.value = value;
}
})
};
};
// test/demo.js
test('module content', function (instance) {
ok(instance.web_tests_demo.value_true, "should have a true value");
var type_instance = new instance.web_tests_demo.SomeType(42);
strictEqual(type_instance.value, 42, "should have provided value");
});
* 显示图片
<img class="oe_kanban_image" src ="data:image/png; base64,${replace this by base64}" />
* Web Components (Action manager)
# 看一列子:
<record model="ir.actions.act_window" id="message_of_the_day_action">
<field name="name">Message of the day</field>
<field name="res_model">message_of_the_day</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="message_day" name="Message of the day" parent="petstore_menu"
action="message_of_the_day_action"/>
对应下的js 处理,没人xml快捷,但用js更灵活
instance.oepetstore.PetToysList = instance.web.Widget.extend({
template: "PetToysList",
start: function() {
var self = this;
new instance.web.Model("product.product").query(["name", "image"])
.filter([["categ_id.name", "=", "Pet Toys"]]).limit(5).all().the
n(function(result) {
_.each(result, function(item) {
var $item = $(QWeb.render("PetToy", {item: item}));
self.$el.append($item);
$item.click(function() {
self.item_clicked(item);
});
});
});
},
item_clicked: function(item) {
this.do_action({
type: 'ir.actions.act_window',
res_model: "product.product",
res_id: item.id,
views: [[false, 'form']],
target: 'current',
context: {},
});
},
});
#上面是窗体Action 下面看焉 client Action
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');
@ instance.web.client_actions 是一个 Registry类的实例
要打开关键字是petstore.home的action ,就实例化instance.oepetstore.HomePage
@对应的菜单
<record id="action_home_page" model="ir.actions.client">
<field name="tag">petstore.homepage</field>
</record>
<menuitem id="home_page_petstore_menu" name="Home Page" parent="petstore_menu"
action="action_home_page"/>