2015年10月28日 星期三

dojo Creating Template Widget

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 的步驟還算複雜,最好多多修改範例程式,並檢查結果,以充分瞭解其中的細節。

沒有留言:

張貼留言