顯示具有 dijit 標籤的文章。 顯示所有文章
顯示具有 dijit 標籤的文章。 顯示所有文章

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

2015年9月29日 星期二

dojo Create Dijit

dojo Create Dijit

簡介

dojo toolkit 為網頁應用的開發提供了一整組的使用者介面元件 (widget)。相對於大多數的開發工具稱它們的使用者介面元件為 widget,dojo 稱它自己的使用者介面元件為 dijit;因此,所有以 dijit 開頭的模組都是使用者介面元件。

使用 dojo 建立使用者介面元件的寫法有二種:

  • 宣告法 (Declarative)
  • 程式法 (Programmatic)

這兩種方法各有其優缺點,略述如下:

宣告法需經過 dojo 的剖析器 (dojo/parser) 對 HTML 做剖析以產生介面元件,執行速度上比較慢;而且在剖析的過程中,介面元件的外觀會先以流覽器預設的樣式顯示,然後再以被 dojo 「整容」後的外觀顯示。在這個過程中,畫面會有所謂「閃動」的情況,很多使用者不喜歡這種經驗。但宣告法在設計網頁時比較直覺,可以很迅速的產生網頁的雛型,以檢查網頁的佈置、介面元件的外觀及網頁的基本功能。

程式法則在程式中直接產生介面元件,隨後將元件掛載到 HTML 中的掛載點。優點是速度快,休沒有畫面「閃動」的情形;但設計網頁時,對元件的佈置不夠直覺,需要花較多的時間調整程式。

在實務上開發應用程式時,都會兩種方式混用;或分階段使用,也就是初期先以宣告法製作雛型,調整完頁面後,再以程式法改寫。有時也會視不同網頁的需求,各自採取不同的寫法。有些介面元件的的設計,在先天上就比較適合其中的某種寫法。像輸入表格就比較偏向使用宣告法製作。

以下的範例為利用 dojo 建立使用者介面的基礎方法,範例中共使用了二種宣告法和一種程式法。實際上,不論是宣告法或是程式法都有很多種的變異,也無法一一例舉。有了基本功後,就可以依需求自行變化了。

Script

<!DOCTYPE html>
<html lang="zh-Hant-TW">
<head>
dijit basic



</head>
<body class="claro">
</body>
    
    
    
    
    
    
</body>

</html>

第 7 行是 dojo 環境設定的一種簡式寫法,如果程式中沒有用到 dojo toolkit 以外的套件(package),或是用到自訂的模組,在為了瞭解介面元件的用法而寫一些簡單的測試網頁時,這樣寫比較省事;但真正寫應用系統時,這種簡式寫法通常是派不上用場的。
第 12 行到第 13 行是第一種宣告法,寫法和一般的 HTML 寫法差不多,只是在標記 (tag) 中多加了 data-dojo-type='dijit/form/Button' 的自訂屬性。告訴 dojo 的剖析器 (parser) 要取代這段 HTML 碼,在這裡放一個 dojo 的按鈕介面元件。這種寫法在流覽器 (browser) 無法下載 dojo 套件時,網頁的內容會以預設的樣式呈現;不過版面都亂了,也沒多大用處。但如果是要從 dojo 轉成使用其它的前端套件時,網頁的基本架構都在,有機會省點改寫的時間。
第 14 行到 19 行是第二種宣告法,這種方法就把宣告法用到極致。但是 HTML 碼和 dojo 的宣告及 Javascript 程式碼都混在一起,將來維護時就很費工夫了;要打的字又多,字打得多,出錯的機率就高;特別是雙引號、單引號混成一堆,也不容易看得清楚。夾在中間的 Javascript 函式也不能再利用,個人實在不認為是個好方法。
第 21 行算是給使用者介面的元件先定位,找個掛載點。
第 27 行指定定要下載 dijit/form/Button 模組,因為在後繼的 Javascript 程式碼中要用到。
第 32 行是 dijit/form/Button 傳給回呼函式時的參數名稱,如果是個類別 (Class) 模組,習慣上參數名稱的第一個字母會大寫。在 Javascript 程式中用這個名字來參考該模組。
第 37 行到第 43 行就是程式法的寫法了。
第 43 行呼叫 startup() 方法,讓 dijit 在網頁中成像 (rendering)。對於 Button 這種簡單的介面元件,有沒有呼叫 startup() 方法並不會造成任何差異,介面元件都會正常顯示,因為在模組中都會預設去呼叫 startup() 函式。但對於某些複雜的介面元件,特別是那種介面元件中又包含了其它的很多介面元件時,如果沒有呼叫 startup() 方法,在螢幕上可能就什麼都看不到,或是一團亂的情況。有時改了介面元件的一些屬性值時,也有可能需要重新呼叫 startup()方法,把介面元件再成像一次。所以,在介面元件建立後固定呼叫 startup(),算是個好習慣吧!