1.相關庫/框架
主要:jQuery(使用1.8.3,如果使用新版本,其他jQuery插件也要升級或修改)、Underscore、QWeb
其他:都在addons\web\static\lib路徑下。
2.示例框架
下載(需要先安裝bzr):bzr branch lp:~niv-openerp/+junk/oepetstore -r 1
下載后將路徑加到OpenERP服務器的addons_path參數中,重啟服務器、更新模塊列表再安裝。
在__openerp__.py中通過:
'js': ['static/src/js/*.js'], 'css': ['static/src/css/*.css'], 'qweb': ['static/src/xml/*.xml'],
將所有js/css/xml(QWeb模板)文件包含進來。
oepetstore/static/js/petstore.js注釋說明:
openerp.oepetstore = function(instance) { // OpenERP模型,必須和模塊名稱相同。instance參數是OpenERP Web Client自動加載模塊時傳入的實例。 var _t = instance.web._t, _lt = instance.web._lt; // 翻譯函數 var QWeb = instance.web.qweb; // QWeb實例 instance.oepetstore = {}; // instance實例里面的模塊命名空間(namespace),比如和模塊名稱相同。 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'); // 將自定義首頁部件與菜單動作綁定 }
可以在網址后面加“?debug”參數使腳本不壓縮以便於調試,例如:
3.類的定義
從instance.web.Class基類擴展:
構造函數名為init();使用this訪問對象實例的屬性或方法:
類可以通過extend()方法繼承;使用this._super()調用基類被覆蓋的方法。
4.部件(Widget)
從instance.web.Widget擴展自定義部件。HomePage首頁部件見petstore.js。
在自定義部件中,this.$el表示部件實例的jQuery對象,可以調用jQuery方法,例如:
往部件中添加一個<div>塊及內容。
部件中可以插入其他部件進行組合:
父子部件可以通過getChildren()、getParent()進行互相訪問。如果重載部件的構造函數,第一個參數必須是父部件,並且必須傳遞給基類。
如果作為頂層部件創建,parent參數應該是null。
部件實例可以調用destroy()方法銷毀。
5.QWeb模板引擎
QWeb模板在XML屬性上加前綴“t-”表示:
t-name:模板名稱;
t-esc:引用實例參數,可以使用任意JavaScript表達式;
t-raw:引用原始實例參數,如果有html標記則保留。
QWeb模板下面的根元素最好只有一個。
oepetstore/static/src/xml/petstore.xml:
定義一個名為“HomePageTemplate”的模板。
使用方法1:
使用方法2:
模板里面的條件控制t-if:
枚舉t-foreach和t-as:
屬性賦值,在屬性名前加前綴“t-att-”:
將input控件的value屬性賦值為“defaultName”。
部件開發示例,顯示產品列表。
JavaScript腳本:
QWeb模板:
CSS樣式:
6.部件事件與特性
部件特性(Properties)的使用:
7.部件訪問 簡化jQuery選擇器:
等於
因此事件的綁定:
可以簡化為:
進一步,可以通過部件提供的events字典屬性簡化為:
注意:這種方法只是綁定jQuery提供的DOM事件機制,不能用於部件的on語法綁定部件自身的事件。 event屬性的鍵名由兩部分組成:事件名稱和jQuery選擇器,用空格分開。屬性值是響應事件的函數(方法)。
事件使用示例:
JavaScript腳本:
QWeb模板:
http://localhost:8069/?debug
3.類的定義
從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();
從instance.web.Widget擴展自定義部件。HomePage首頁部件見petstore.js。
在自定義部件中,this.$el表示部件實例的jQuery對象,可以調用jQuery方法,例如:
this.$el.append("<div>Hello dear OpenERP user!</div>");
部件中可以插入其他部件進行組合:
instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({ init: function(parent, name) { this._super(parent); this.name = name; }, });
部件實例可以調用destroy()方法銷毀。
5.QWeb模板引擎
QWeb模板在XML屬性上加前綴“t-”表示:
t-name:模板名稱;
t-esc:引用實例參數,可以使用任意JavaScript表達式;
t-raw:引用原始實例參數,如果有html標記則保留。
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;"> <div>Hello <t t-esc="name"/></div> <div><t t-esc="3+5"/></div> <div><t t-raw="some_html"/></div> </div> </t> </templates>
使用方法1:
instance.oepetstore.HomePage = instance.web.Widget.extend({ start: function() { this.$el.append(QWeb.render("HomePageTemplate")); }, });
<t t-if="true == true"> true is true </t> <t t-if="true == false"> true is not true </t>
<t t-foreach="names" t-as="name"> <div> Hello <t t-esc="name"/> </div> </t>
<input type="text" t-att-value="defaultName"/>
部件開發示例,顯示產品列表。
JavaScript腳本:
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() { var products = new instance.oepetstore.ProductsWidget(this, ["cpu", "mouse", "keyboard", "graphic card", "screen"], "#00FF00"); products.appendTo(this.$el); }, }); instance.oepetstore.ProductsWidget = instance.web.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'); }
<?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> <t t-name="ProductsWidget"> <div> <t t-foreach="widget.products" t-as="product"> <span class="oe_products_item" t-att-style="'background-color: ' + widget.color + ';'"><t t-esc="product"/></span><br/> </t> </div> </t> </templates>
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() { // 在按鈕上綁定click事件 self.trigger("user_choose", true); // 觸發自定義user_choose事件,傳遞事件參數true/false }); 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); // 在部件上綁定user_choose事件到響應函數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.widget.on("change:name", this, this.name_changed); //綁定name特性的change事件 this.widget.set("name", "Nicolas"); // 設置特性值 var getedname = this.widget.get("name"); // 讀取特性值
this.$el.find("input.my_input")
this.$("input.my_input")
this.$el.find("input").change(function() { self.input_changed(); });
instance.oepetstore.MyWidget = instance.web.Widget.extend({ events: { "click .my_button": "button_clicked", }, button_clicked: function() { .. } });
事件使用示例:
JavaScript腳本:
openerp.oepetstore = function(instance) { var _t = instance.web._t, _lt = instance.web._lt; var QWeb = instance.web.qweb; instance.oepetstore = {}; instance.oepetstore.ColorInputWidget = instance.web.Widget.extend({ template: "ColorInputWidget", start: function() { var self = this; this.$el.find("input").change(function() { self.input_changed(); }); self.input_changed(); }, input_changed: function() { var color = "#"; color += this.$el.find(".oe_color_red").val(); color += this.$el.find(".oe_color_green").val(); color += this.$el.find(".oe_color_blue").val(); this.set("color", color); }, }); instance.oepetstore.HomePage = instance.web.Widget.extend({ template: "HomePage", start: function() { this.colorInput = new instance.oepetstore.ColorInputWidget(this); this.colorInput.on("change:color", this, this.color_changed); this.colorInput.appendTo(this.$el); }, color_changed: function() { this.$el.find(".oe_color_div").css("background-color", this.colorInput.get("color")); }, }); instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage'); }
<?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> <t t-name="ColorInputWidget"> <div> Red: <input type="text" class="oe_color_red" value="00"></input><br /> Green: <input type="text" class="oe_color_green" value="00"></input><br /> Blue: <input type="text" class="oe_color_blue" value="00"></input><br /> </div> </t> <t t-name="HomePage"> <div> <div class="oe_color_div"></div> </div> </t> </templates>
.oe_color_div { width: 100px; height: 100px; margin: 10px; }
8.修改已有的部件和類
可以用include()方法重載已有的部件和類,這個和繼承機制類似,是一種插入的方法:
9.與服務器的交互-讀取數據模型
客戶端使用Ajax與服務器交互,不過OpenERP框架提供了簡化的方法,通過數據模型進行訪問。
OpenERP自動將服務端的數據模型轉化為客戶端端模型,直接調用即可。服務器上petstore.py里面的模型:
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.CompoundContext()}).then(function(result) { self.$el.append("<div>Hello " + result["hello"] + "</div>"); // will show "Hello world" to the user }); }, });
模型的call()方法參數:
第一個參數name是方法的名稱;
第二個參數args是按照順序排列的參數數組。OpenERP定義的模型方法前三個參數(self, cr, uid)是固定的,由框架產生,也就是說傳遞的參數數組從第四個開始插入。而context又是特殊的。例子:
方法定義:
def my_method2(self, cr, uid, a, b, c, context=None):
第三個參數kwargs為命名參數,按照名稱傳遞給Python的方法參數。例如:
CompoundContext類提供用戶的語言和時區信息。也可以在構造函數中添加另外的數據:
10.與服務器的交互-查詢
客戶端數據模型提供了search()、read()等方法,組合為query()方法,使用例子:
model.query(['name', 'login', 'user_email', 'signature']) .filter([['active', '=', true], ['company_id', '=', main_company]]) .limit(15) .all().then(function (users) { // do work with users records});
filter():指定OpenERP 域(domain),也即過濾查詢結果;
limit():限制返回的記錄數量。
最后調用查詢對象的all()方法執行查詢。
查詢異步執行,all()返回的是一個deferred,因此要用then()提供回調函數來處理結果。
數據模型的查詢是通過rpc調用實現的。
示例1:顯示每日提示
JavaScript腳本:
服務器端從OpenERP的產品表繼承一個類(模型):
JavaScript腳本:
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); }); }); }, }); }