dojo 除了提供一系列豐富的圖形介面的元件,也允許程師設計師自行建構圖形介面元件以符合應用的特殊需求。dijit/_WidgetBase 是所有圖形介面元件的基礎,所有的圖形介面元件都繼承自 dijit/_WidgetBase。要能夠在 dojo 中自行開發圖形介面元件,深入瞭解 dijit/_WidgetBase 是絕對必須的。
首先,dijit/_WidgetBase 預先定義了一些每個圖形介面元件都會繼承的共同屬性:
- id:
- 用來辨識圖形介面元件的唯一名稱,型態為字串。可以自行指定或由 dijit 自行產生。
- lang:
- 指定取代 dojo 預設使用的語言,這個屬性很少用到。
- dir:
- 如果指定語言是從右至左,就用這個屬性來指定。
- domNode:
- 圖形介面主要的 DOM 元素(Element),多數的圖形介面元件都會修改 HTML 的結構,這個屬性可以參考到組成圖形介面最終的 HTML 元素及節點。
- class:
- 圖形介面元件 domNode 之 HTML 的 class 屬性。
- style:
- 圖形介面元件 domNode 之 HTML 的 style 屬性。
- title:
- 圖形介面元件 domNode 之 HTML 的 title 屬性。
- baseClass:
- 圖形介面元件在 CSS 中的基本 class 屬性。
- srcNodeRef:
- 圖形介面元件在 DOM 中的原始掛載點,某些圖形介面在建構時,這個掛載點會被重設或清除。
另外,dijit/_WidgetBase 定義了數個函式,藉以掌控整個圖形介面元件的生命週期,各函式名稱依執行先後次序列示如下:
- constructor() // common to all prototypes, called when instantiated
- postScript()
- postMixInProperties()
- buildRendering()
- postCreate()
- startup()
以下介紹各函式的作用:
- constructor()
- 是所有類別都具備的函式,在實體化物件時,會被呼叫一次。
-
- postScript()
-
- 會先呼叫 create() 函式,在 create() 函式中又會依次呼叫 postMixInProperties(), buildRendering(), 及 postCreate() 三個函式。
- postMixInPorperties()
- 作用在將建立元件時所傳入的屬性參數或函式融入 (mixin) 到既有的屬性及函式中。
- buildRendering()
- 作用在建構所有介面的 HTML 元素,並設定 domNode 的值,這個函式執行完畢時,圖形介面的 HTML 元素都己建構並設定完畢,但尚未將這些元素加入到 HTML 主文件中。
- postCreate()
- 這個算是最重要的函式了,在所有的屬性都定義完畢,而且圖形介面相關的 HTML 段落都己建置完成,且加入 HTML 主文件之後,(將 HTML 段落加到 HTML 主文件的動作,在呼叫完 buildRendering() 和呼叫 postCreate() 兩函式之間完成),才會執行這個函式。但要注意的是,如果此元件有子元件時,子元件在此刻未必己經加到主元件中,因此,在此階段還不能進行元件最終尺寸的計算。
- startup()
- 是第二重要的函式了,在圖形介面相關的 HTML 段落加入主文件後,同時所有的子圖形介面都己完成之後,這個函式才會執行。用程式法建立圖形介面元件時,最好在建立完畢後呼叫 startup() 函式,以確保所有的動作都完成。
另一個非常重要的工具是 dijit/_TamplatedMixin。有些複雜的圖形介面元件,其 HTML 段落非常的複雜,如果要靠 API 一步一步組成最終的 HTML 碼,實在是曠日費時,而且容易出錯。如果能直接寫成最終的 HTML 碼,直接掛載到預定的位置,那就容易多了。而 dijit/_TemplatedMixin 就職司此項工作;它可以讀取一個含有 HTML 段落的檔案,將其內容處理後掛載到 HTML 主文件上的指定掛載點,更進一步的還提供了參數取代的功能,可直接將屬性的定義以參數取代的方式代入 HTML 碼中。
在 HTML 的模版 (template) 中置入 ${propName} 字串,dijit/_TemplatedMixin 便會用名為 propName 的屬性值,來取代這個字串。
在模版中,還可以定義兩個屬性:
- data-dojo-attach-point="nameNode"
- 會在 widget 中建立一個名為 nameNode 的屬性,其值可以直接參考到這個 DOM 元素。
- data-dojoattach-event="onClick:handler"
- 用在將這個 DOM 元素連接到觸發的事件 (click),並呼叫事件的處理函式 handler()。
除此之外,dijit/_TemplaedMixin 還會為 widget 預設建立兩個屬性值:
- domNode:
- 這個屬性值可以直接參考到 widget 中最主要的 DOM 元素。
- containerNode:
- 如果這個 widget 有包含其它的子圖形介面元件,用這個屬性可以直接參考到子元件的 DOM 元素。
如果在 HTML 的模板 (template)中又包含了其它的 dijit,此時,必須另外 require 另一個名為 "dijit/_WidgetsInTemplateMixin" 的模組,同時,用到的 dijit 模組也必須 require 進來。
當使用用外部定義的 HTML 檔案時,要藉著 dojo/text 這個插件 (plug-in) 將檔案內容讀進來。
首先,先根據圖形介面所要求的外觀,建一個新檔,由網頁所在位置起始,檔案的相對路徑位置名為 "js/app/widgets/templates/MyCounter.html"。因為用 SyntaxHighlighter 在處理 ${baseClass}
時會變成 ${baseClass}=""
,所以這一段程式碼不用 SyntaxHighLighter 處理。內容如下:
<div ${baseClass}
data-dojo-attach-point="buttonNode"
data-dojo-attach-event="onClick:_increment">
<div
data-dojo-attach-point="counterNode"
style="padding:10px 0px; font-size:48px; text-align:center}"></div>
</div>
在模版中,定義了兩個 data-dojo-attach-point,一個名為 "buttonNode",另一個名為 "counterNode",這樣在程式中比較好操作。此外,定義了一個 data-attach-event,在元件觸發點按事件時,呼叫 _increment() 處理函式。另有一個屬性變數 ${baseClass} ,但程式中並沒有針對 baseClass 屬性定值,所以並不會在最終的 HTML 裡,產生任何 class="someClasses" 的設定。
其次,建立一個新檔,由網頁所在位置起始,檔案的相對路徑為 "js/app/widgets/MyCounter.js"。
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dojo/text!./templates/MyCounter.html",
"dojo/dom-style"
],
function(
declare,
_WidgetBase,
_TemplatedMixin,
template,
style
){
return declare("MyCounter", [_WidgetBase, _TemplatedMixin], {
// counter
value: 0,
baseClass:"",
width:"64px",
height:"64px",
backgroundColor:"yellow",
border:"10px solid blue",
borderRadius:"10px",
templateString:template,
constructor: function(){
this.inherited(arguments);
},
postscript:function(){
this.inherited(arguments);
},
// minin with the parameters passed in
postMixInProperties:function(){
this.inherited(arguments);
},
// create the DOM for this widget
buildRendering: function(){
this.inherited(arguments);
},
// setup the style of DOM node
postCreate: function(){
var bNode = this.buttonNode;
style.set(bNode, "width", this.width);
style.set(bNode, "height", this.height);
style.set(bNode, "background-color", this.backgroundColor);
style.set(bNode, "border", this.border);
style.set(bNode, "border-radius", this.borderRadius);
style.set(bNode, "text-align","center");
var cNode = this.counterNode;
cNode.innerHTML = 0;
this.inherited(arguments);
},
startup:function(){
this.inherited(arguments);
},
// every time the user clicks the button, increment the counter
_increment: function(){
this.counterNode.innerHTML = ++this.value;
},
_getValuerAttr:function(){
return this.value;
},
_setValueAttr:function(number){
this.value = number;
this.counterNode.innerHTML = this.value;
}
});
});
第 5 行用 dojo/text 從相對路徑為名為 "./templates/MyCounter.html" 檔案中讀入 HTML 段落的內容。 放在 template 變數中,然後在第 24 行將其設定成 templateString 屬性的值。
第 17~24 行設定各屬性值。
第 41~49 行設定 domNode 的各項外觀的屬性。
第 56~58 行定義事件處理器。
第 59~65 行定義兩個供外部使用的函式,一個用來讀取屬性 value 的值,有就是計數器的值,另一個用來設定。要注意的是這兩個函式的命名方式,都是 _get${PropName}Attr() 及 _set${PropName}Attr(),其中 ${PropName} 字串,用屬性的名稱來取代。
最後,則是主網頁中的內容。
建立好自定的 widget 之後,用法就和 dijit 中的圖形介面元件的用法一樣,唯一要注意的是元件的路徑名,必須正確。
第 18~25 行在主控台中印出各個和 DOM node 有關之屬性的值,記得到主控台去檢查印出值,必詳細思考一下。建立 widget 的步驟還算複雜,最好多多修改範例程式,並檢查結果,以充分瞭解其中的細節。