dojo Creating Template Widget
簡介
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) 將檔案內容讀進來。
範例
範例中,自定一個圖形介面元件,元件的作用是計數器,每點一下元件,其中的數字便會加 1。這個範例設計的並不是很完善,主要的作用是在解釋如何建立一個自定的圖形介面而己。例如:如果不改變元件的大小,元件只能容納 2 位數。如果改變元件的大小,字形不會跟著變化,位置也不會保持在中間等等。
CSS
<style> html{ width:100%; height:100%; } body { margin:0 auto; width:1000px; height:100%; padding-top: 20px; } </style>
屬性都在 widget 中定義了,所以 CSS 中沒有什麼要寫的。
HTML
<body class="claro">My Counter
</body>
HTML 中要設定一個掛載點,注意 data-dojo-type 屬性的值。
HTML
首先,先根據圖形介面所要求的外觀,建一個新檔,由網頁所在位置起始,檔案的相對路徑位置名為 "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 的步驟還算複雜,最好多多修改範例程式,並檢查結果,以充分瞭解其中的細節。
沒有留言:
張貼留言