Odoo 自定義Widgets 基礎教程(章節2)
大家好,
本文將進一步解釋odoo widget 的相關原理,其中首先會講述:1. 什麼是widgets 中的繼承;2. 為什麼有很多 init : function 語法;3. widgets 系統結構;
首先,讓我們來看看結構:(來自 /addons/web/ 目錄)
問題一:apps.js 是幹啥的?
下面是 app.js 的結構:(說明見圖)
下面是 core 目錄的結構:
這裡,我們來回答一下最開始的問題:1. 什麼是widgets 中的繼承;2. 為什麼有很多 init : function 語法;3. widgets 系統結構;
問題1(解答):
問題2(解答): 這裡首先要了解,mixin在javascript是什麼。minxin可以看作是一種從別的物件”借用”功能的方法。每一個新定義的物件都有一個 prototype屬性,其他的物件就可以從這裡”借用”功能。這裡的功能可以是一個屬性,也可以是一個方法。
mixins這種借用在 javascript裡非常的適用。在重用程式碼的時候可以使用mixins來實現繼承,也可以達到類似多繼承的效果。假設我們定義了這麼一個物件:
var myMixins = {
moveUp: function(){ console.log( "move up" ); },
moveDown: function(){ console.log( "move down" ); },
stop: function(){ console.log( "stop! in the name of love!" ); }
};
我們可以非常容易的使用一個helper來擴充套件現有的物件。比如使用Underscore.js
的extend()
方法:
// carAnimator 構建器的一個骨架 function carAnimator(){ this.moveLeft = function(){ console.log( "move left" ); }; } // personAnimator構建器的一個骨架 function personAnimator(){ this.moveRandomly = function(){ /*..*/ }; } // 用我們的 Mixin拓展這些類 _.extend( carAnimator.prototype, myMixins ); _.extend( personAnimator.prototype, myMixins ); // 建立carAnimator 的新例項 var myAnimator = new carAnimator(); myAnimator.moveLeft(); myAnimator.moveDown(); myAnimator.stop(); // 輸出: // move left // move down // stop! in the name of love!
從程式碼可以看到,這個mixins實現的非常簡單。在下一個例子中我們會使用兩個建構函式:一個Car
,一個Mixin
。我們要做的就是使用一個自定義的argument方法來擴充套件Car,這樣Car
可以從Mixin
裡”借用”某些特定的方法。比如,driveForward()
和driveBackword()
。這次我們不使用Underscore.js
。這裡例子會非常清楚的展示argument方法是怎麼達到”借用”效果的:
// 定義一個簡單的 Car 構建器
var Car = function ( settings ) {
this.model = settings.model || "no model provided";
this.color = settings.color || "no colour provided";
};
// Mixin
var Mixin = function () {};
Mixin.prototype = {
driveForward: function () {
console.log( "drive forward" );
},
driveBackward: function () {
console.log( "drive backward" );
},
driveSideways: function () {
console.log( "drive sideways" );
}
};
// 使用另一個方法擴充套件現有物件
function augment( receivingClass, givingClass ) {
// 只提供某些方法
if ( arguments[2] ) {
for ( var i = 2, len = arguments.length; i < len; i++ ) {
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
}
}
// 提供所有方法
else {
for ( var methodName in givingClass.prototype ) {
// 檢查以確保接收類沒有相同的名稱
if ( !Object.hasOwnProperty(receivingClass.prototype, methodName) ) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
// 或者:
// if ( !receivingClass.prototype[methodName] ) {
// receivingClass.prototype[methodName] = givingClass.prototype[methodName];
// }
}
}
}
// Augment the Car constructor to include "driveForward" and "driveBackward"
augment( Car, Mixin, "driveForward", "driveBackward" );
// Create a new Car
var myCar = new Car({
model: "Ford Escort",
color: "blue"
});
// 測試以確保我們現在可以訪問這些方法
myCar.driveForward();
myCar.driveBackward();
// 輸出:
// drive forward
// drive backward
// 我們還可以增加 Car 類來包含我們 mixin 中的所有方法
// 而不是僅僅監聽某些加強( Car, Mixin );
var mySportsCar = new Car({
model: "Porsche",
color: "red"
});
mySportsCar.driveSideways();
// 輸出:
// drive sideways
Mixins可以減少程式碼的重複增加程式碼的複用。
問題3(解答):
var MyWidget = Widget.extend({
// 用於渲染的QWeb模板的名稱
template: "MyQWebTemplate",
init: function (parent) {
this._super(parent);
// 渲染前要初始化的內容
},
willStart: function () {
// 在小部件準備就緒之前需要完成的非同步工作
// 此方法應返回延遲
},
start: function() {
// 渲染後要製作的內容, `this.$el` 保持正確的值
this.$(".my_button").click(/* 事件繫結示例 * /);
// 如果您有一些非同步操作,最好在start()返回一個Promise物件,它代表了一個非同步操作的最終完成或者失敗
// 當然這很少見, and if you
// 如果您想獲取一些資料,這個步驟可能需要在
// willStart 方法中完成
var promise = this._rpc(...);
return promise;
}
});