2015年9月30日 星期三

dojox/form/Manager (part 2)

dojox/form/Manager (part 2)

簡介

這篇還是講 dojox/form/Manager(表單管理員)。上篇介紹 dojox/form/Manager 時,所用的範例裡,所有的輸入欄位都用宣告法建立,表單的處理也很簡單。但碰到比較複雜的表單時,有些欄位要和其它欄位連動,因此得用程式法動態建立輸入欄位,這時又該如何處理呢?

用宣告法所建立的表單管理員及其它位於表單管理員容器中,也就是 <form></form> 之間,用宣告法所建立的輸入欄位,在剖析器 (parser) 對 HTML 進行剖析時,剖析器會同時將這些輸入欄位的 dijit 納入表單管理員的管理。但是那些透過程式法建立的輸入欄位,在剖析器剖析 HTML 時並不存在,因此也無法納入表單管理員的管理。所以,在用程式法建立完輸入欄位後,要跟表單管理員註冊 (register),表單管理員才知道要將這個新建的 dijit 納入管理。如果又動態的將該欄位刪除,也得向表單管理員取消註冊 (unregister),以脫離表單管理員的管理。因為向表單管理員註冊,就會被納管,所以 dijit 的掛載點就算不在表單管理員的容器之中也沒有關係。

範例

這個範例的情境和上篇一樣,不同的是,只用程式法建立了一個文字輸入欄位,不管輸入什麼值,按下 submit 按鈕後,欄位值都會被改成 Blablabla。

CSS

<style>
input, textarea {
    font-family:monospace;
}
</style>

上列的 CSS 碼將輸入欄位的字型改成定寬 (Fixed-Width) 字型 ,這樣欄位的寬度比較好計算。

HTML

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

Script


第 20 行定義 name 屬性,這個屬性一定要給,而且一定得在這裡給,不然 gatherFormValues() 會抓不到這個欄位的值。要特別注意的是,在 HTML 這個 dijit 的掛載點那裡不能給 name 屬性,不然只有 formWidgetValue() 可以取得該欄位的值,gatherFormValues() 卻抓不到。

第 25~26 行將這個 dijit 向表單管理員註冊納管。如果要取消註冊,就用 form.unregesterWidget(word) 來取消註冊。表單管理員納管的所有 dijit,都放在表單管理員的 formWidgets 屬性下。

其它的用法,就沒什麼兩樣了。

2015年9月29日 星期二

dojox/form/Manager (part 1)

dojox/form/Manager (part 1)

簡介

表單管理員的模組 dojox/form/Manager ,是用來管理單一輸入表單的工具。模組名最前面的目錄是 dojox,dojox 最後的字母 "x" 表示目錄下的模組是 dojo 的延伸 (extension) 或實驗 (experiment) 性質的模組。雖然被放置在 dojox 目錄下,但 dojox/form/Manager 這個模組己經成熟、穩定到可以應用在正式的開發上了。

dojox/form/Manager 應用時以使用宣告法(Declarative) 為主。表單中的欄位也以使用宣告法建立時最為便利。範例中是一個很簡單的表單管理的應用,實際上,表單管理者的功能還頗強大的,也能應付複雜和動態的表單。

範例

範例中建立了一個很簡單的 form 輸入表格,只有三個欄位及二個按鈕。

CSS

<style>
input, textarea {
    font-family:monospace;
}
</style>

上列的 CSS 碼將輸入欄位的字型改成定寬 (Fixed-Width) 字型 ,這樣欄位的寬度比較好計算。

HTML

<body class="claro">
    






</body>

在 HTML 中,我們建立了三個欄位,二個文字欄位,一個數字欄位,及二個按鈕,做為測試之用。

Script


第 2 行先建立個全域變數(Global Variable) 名為 app。這個變數會出現在流覽器中的 window 變數之下,全名是 window.app;隨後所定義的變數和函式都掛載在這個變數之下。這樣在 Chrome 的開發者工具中,要找到應用程式中所定義的變數,就比較容易了。

第 17 行的 gatherFromValues(["lastName", "firstName"])會讀取表單中 name 屬性值為 lastName 及 firstName 兩個欄位的輸入值,並傳回一個包含這兩個欄位的 Javascript 物件。如果要讀取更多的欄位,就在參數的陣列中加入要讀取欄位的 name 屬性值。如果沒給任何參數,gatherFromValues()會讀取表單中所有欄位的值,並傳回含有所有欄位的 Javascript 物件。當沒有預設值,同時也沒有輸入任何值時,文字欄位傳回空字串 "",數字欄位傳回 NaN (Not a Number)。

第 23 行指派了一個值給 submitData.Salary,假裝 submitData 是由伺服器收下來的查詢資料,要將查詢值寫回表單裡。

第 24 行的 setFormValues(submitData)中的參數 submitData 必須是一個 Javascript 的物件,setFormValues() 函式,會將參數物件中的 name/value 項目中的 name 和各欄位 name 屬性的值做對應,如果有對應,則將值指派給該對應欄位的 value 屬性值。如果找不到對應 name 屬性值的欄位,則忽略參數中的該筆 name/value 項目。寫起來很冗長,不容易說清楚,執行一下範例程式就很容易明白了。

第 28 行的 reset() 會將表單的各欄位設回初始值。

這樣,一般輸入表單所需要做的三件大事就可以搞定了。測試結果要到「開發者工具」裡的「主控台」看。

另外, var lastName = form.formWidgetValue("lastName"); 可單獨取得 name="lastName" 輸入欄位的值,也就是該欄位之 value 屬性的值。 form.formWidgetValue("lastName", "Smith"); 則將 name="lastName" 輸入欄位之 value 屬件的值。

dijit/form/NumberTextBox

dijit/form/NumberTextBox

簡介

數值文字框的模組是 dijit/form/NumberTextBox ,用來輸入數值,同時對數值做檢核。

數值文字框為檢核文字框 (dijit/form/ValidationTextBox) 的子類別 (subclass),因此繼承了檢核文字框的所有屬性。 實際上,有些屬性在檢核文字框的類別中就已經存在了;只不過在輸入數值時,也就是使用數值文字框時,才真正派上用場;像 constrains 這個屬性就是如此,其中的大部份的限制條件及格式都是針對數值使用的。

範例

數值超出允許允範圍的錯誤訊息:

CSS

<style>
input, textarea {
    font-family:monospace;
}
</style>

上列的 CSS 碼將輸入欄位的字型改成定寬 (Fixed-Width) 字型 ,這樣欄位的寬度比較好計算。

HTML

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

type 屬性在數值文字框時,設定成 type="text" 或 type="number" 都可以,因為在經過 dojo 的「整容」後,最終 HTML 碼的設定都是 type="text"。

當套用 dojo 的外觀時, HTML 中的 size 屬性不起作用,一定得用 style 去設定欄位寬度,實驗結果大約是:(字母數 * 0.7em )。如果修改了 font-size 屬性,得再做實驗了。

constrains 用來定義對數入數值的限制,中的 min 和 max 屬性,望文生義,就知道是設定輸入數值的最小允許值和最大允許值,允許值的範圍含括 min 及 max 的設定值。

places 指定輸入數值時,數值中必須包含幾位小數。如果不設,就不會針對輸入的數值做小數位個數的檢核。如果在輸入框中輸入數值時,因為小數位的值是 0 而沒有輸入小數位,一樣會造成格式錯誤,並顯示格式錯誤的訊息。

pattern 說明當數值輸入後,重新顯示在輸入框裡的格式。在這個範例中實在沒有多大意義,但如果是金額的輸入時,就大有用處了。寫在這裡,只是提醒自己有這個屬性的存在,可以在輸入數值後,改變數值的顯示格式,然後再一次顯示在數值輸入框中,讓使用者確認。

proptMessage 的訊息為欄位獲得焦點 (focus) 的提示輸入訊息。

missingMessage 是在欄位失去焦點 (blur) 卻沒有輸入值時的警告訊息

invalidMessage 欄位的輸入值不合格式要求 (invalid) 時的錯誤訊息。

rangeMessage 是在輸入數值超出允許數值的範圍時,所要顯示的訊息。

tooptipPosition 需給予一個指定方位的陣列,訊息會依陣列中方位的前後次序,在螢幕上尋找合適的空間顯示訊息。範例中給的值是 "['below', 'above']",因此訊息會優先出現在欄位的下方,如果此時欄位已經在流覽器視野 (viewport) 的最下方時,訊息就會採用第二順位的位置,出現在欄位的上方。如果屬性值是 "['after', 'before']" 時,訊息就出現在欄位的右方,右方沒有位置時,就出現在左方,以此類推。也可以把四個方向的值都放到陣列中,讓流覽器在上下左右找個最合適的位置顯示訊息。

訊息在顯示時,用的是 dijit/Tooltip 這個元件,字型的大小比 HTML 預設 title 屬性的字型要大些,看得比較清楚。

Script


dijit/form/ValidationTextBox

dijit/form/ValidationTextBox

簡介

檢核文字框 (ValidationTextBox) 的模組名稱是 dijit/form/ValidationTextBox 。這個模組是用來輸入文字資料,同時對輸入的資料做條件檢核。

網頁應用系統一般的習慣是在前端(流覽器)檢核輸入資料的格式及其正確性,而業務邏輯的正確性則在後端(伺服器)上做檢查。而檢核項目是屬於資料的正確性或是業務邏輯的正確性,則需要小心界定。特別是數值的部份,舉例來說,「購買數量的上限」到底是屬於資料的正確性,還是屬於業務還輯的正確性?如果劃分為資料的正確性而在前端做檢核,當變更購買數量上限時,所有在前端有用到這個欄位的網頁都得要做修正,除非特別為這個欄位建立一個類別 (Class) 而在類別裡定義檢核的條件。如果在後端做檢核,理論上而言,只要修改一個地方即可;但如果後端的程式架構沒有設計好,檢核條件都寫死在各程式中,修改起來,恐怕也很讓人頭痛。

範例

範例中展示一個文字輸入檢核框。

欄位輸入提示訊息:

欄位未輸入值的警告訊息,警告訊息在點按驚嘆號或欄位時才會出現:

欄位值格式錯誤的訊息,每輸入一個字元後都會立即做檢核,一檢查到錯誤,就會出現錯誤訊息:

CSS

<style>
input, textarea {
    font-family: monospace;
}
</style>

input, textarea{} 將輸入欄位的字型改成定寬 (Fixed-Width) 字型,這樣欄位的寬度比較好計算。

HTML

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

當套用 dojo 的外觀時, HTML 中的 size 屬性不起作用,一定得用 style 去設定欄位寬度,而且長度還很不好抓,實驗結果大約是:字母數 x 0.7em。如果修改了 font-size 屬性,也許又得做實驗了。

proptMessage 的訊息為欄位獲得焦點 (focus) 的提示輸入訊息。

missingMessage 是在欄位失去焦點 (blur) 卻沒有輸入值時的警告訊息

invalidMessage 欄位的輸入值不合格式要求 (invalid) 時的錯誤訊息。

tooptipPosition 需給予一個指定方位的陣列,訊息會依陣列中方位的前後次序,在螢幕上尋找合適的空間顯示訊息。範例中給的值是 "['below', 'above']",因此訊息會優先出現在欄位的下方,如果此時欄位已經在流覽器視野 (viewport) 的最下方時,訊息就會採用第二順位的位置,出現在欄位的上方。如果屬性值是 "['after', 'before']" 時,訊息就出現在欄位的右方,右方沒有位置時,就出現在左方,以此類推。也可以把四個方向的值都放到陣列中,讓流覽器在上下左右找個最合適的位置顯示訊息。

訊息在顯示時,用的是 dijit/Tooltip 這個元件,字型的大小比 HTML 預設 title 屬性的字型要大些,看得比較清楚。

regExp 定義欄位值的有效輸入格式,用以檢核欄位的輸入值。

Script


因為用宣告法產生介面元件,Javascript 程式中沒有做其它事。

dijit/form/Button

簡介

按鈕 (Button) 模組的名稱是 dijit/form/Button。在網頁應用中,按鈕是最常見的介面元件。論尺寸,則大小都有;說外觀,則各式各樣。但嚴格來說,作用只有一個;就是讓使用者在按下按鈕後,觸發一個事件,讓應用程式採取必要的回應動作。

範例

範例中以程式法建立四個按鈕,顯示在 Chrome 中時,如下圖所示:

圖 1. Button 範例

CSS

<style>

</style>

CSS 中沒有特殊的樣式設定。

HTML

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

HTML 的部份很簡單,就直接建立四個按鈕的掛載節點。

Script


四個按鈕都是直接用程式產生,沒有用到宣告法,因此沒有必要呼叫剖析器 (parser) 去剖析 HTML。也因為不用等剖析器去產生所有用宣告法產生的介面元件,所以也不用呼叫 ready()。其實連 parser 及 ready 這兩個模組都不用在 require() 中載入,這也是程式法產生介面的好處。
第 12 行及第 19 行到第 21 行,展示了二種用程式法建立按鈕,在按下按鈕觸發點按 (click) 事件時,兩種呼叫函式的寫法。這兩種寫法沒有所謂對錯,或哪一種寫法比較好,全看喜好而定。但第 19 行到第 21 行的寫法,函式不能共用;如果有很多按鈕的行為模式都很像時,也許該用第 12 行的寫法,函式中可以判斷觸發事件 (Event) 的屬性值,判斷是哪一個按鈕觸發事件,再決定該如何處理。要使用哪一種,就和個人寫程式的習慣有關了。

由範例中可以看出,使用 dojo 建立一個一般的按鈕並不難。按鈕的尺寸會依標示 (Label) 的長短及圖像的大小,自行調整。如果要變更按鈕的長相,像是把所有的按鈕都變成等寬,讓畫面好看一些,事情就有點複雜了。

補述

如果由 Chrome 的「開發者工具」去檢視最終的 HTML 碼,一個尚未使用 dojo 「裝飾」過的按鈕,HTML 碼如下:


在用 dojo 裝飾過後,最終的 HTML 碼如下:


  
      
        
        
        
          Click Me
      
  
  

在這麼長串的 HTML 碼中,要用 CSS 去調整按鈕的外觀,一時還真有無從下手的感覺。如果只是要改變按鈕的寬度,有個簡單一點的方法是在 <button> 的標記中,再加一組具有類別 (class) 屬性的 <div> 或 <span> 標記,把按鈕的標示放在其中。例子如下:


再補上 CSS 碼如下:

.equalWidth {
  width:200px;
}

也就可以搞定這件事了,如果還要改變其它的屬性,就得好好研究上面那一大串的 HTML 碼,看看要如何變更其 CSS 的設定。例如要改變標示 (label) 的顏色成為紅色,就得設定:

#btn1_label {
  color:red";
}

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(),算是個好習慣吧!

dojo Template

dojo Tamplate

環境

  • CSS3
  • HTML5
  • ECMA5 Javascript
  • dojo Toolkit 1.10
  • Chrome 43.0

基本模版

1 <!DOCTYPE html>
2 <html lang="zh-Hant-TW">
3 <head>
4 <title>dojo Configuration</title>
5 <meta charset="utf-8" />
6 <link rel="stylesheet" href="js/libs/dijit/themes/claro/claro.css">
7 <script>
8     dojoConfig={
9         async:true,
10         parseOnLoad:false,
11         baseUrl:"js/",
12         packages:[
13            {name:"dojo", location:"libs/dojo"},
14             {name:"dijit", location:"libs/dijit"},
15             {name:"dojox", location:"libs/dojox"},
16             {name:"utils", location:"app/utils"}
17         ]
18     }
19 </script>
20 <script src="https:js/libs/dojo/dojo.js"></script>
21 <head>
22 <body class="claro">
23 // Put HTML and dojo declarative code here
24 </body>
25 <script>
26 var app={};
27 require([
28     "dojo/parser",
29     "dojo/ready"
30     ],
31 function(
32     parser,
33     ready
34 ){
35     parser.parse();
36     ready(1, function(){
37     // 所有用到任何 dijit 的程式碼,都要放在這個函式裡面
38     // 否則在調用 dijit 時,很可能 dijit 當時尚未建立,因而得到 null 值
39     });
40 });
41 </script>
42 <html>

第 6 行載入所要使用的主題樣式,範例裡用的是 claro 主題樣式。
第 9 行的 async:true 設定使用 AMD (Asynchronous Module Definition) 模式。
第 11 行 baseUrl 的值是包含此段程式碼的 HTML 頁面到存放 dojo 套件的基本相對路。換句話說,baseUrl + location 即是 name 套件對 HTML 的完整相對路徑 。例如: dojo 這個套件的相對位置即為 js/libs/dojo ;而 utils 套件的相對路徑即是 js/app/utils。其餘套件的相對路徑,以此類推。
第 20 行的作用在載入最基本的 dojo 模組。
第 22 行指定 body 的 class 為 claro,用以套用主題樣式。這裡 class 的值一定要給,同時要和第 6 行中載入的主題樣式名稱一致。
第 26 行定義一個名為 app 的全域變數 (global variable),用來掛載未來程式中的變數及函式,在流覽器的主控台 (console) 中,可以用 window.app 這個變數名找到它。
第 27 到 30 行,使用 require() 函式,指定程式中所要使用到的 AMD 模組。parser 會在剖析 HTML 碼時,自行下載在 HTML 碼中宣告的 dijit AMD 模組; 因此,在 require() 函式中,就無須特別指定己宣告的 dijit 模組。
第 30 到 32 行,將至載的 AMD 模組以參數的方式,傳入回呼 (callback) 函式中。要注意的是參數的先後次序和 require() 下載 AMD 模組時所列示的先後次序,兩者必須一致。
第 34 行呼叫剖析器 (parser) 對 HTML 碼進行剖析 (parse),同時根據宣告進行 dijit 的建立。在 dojo 1.8 版後,剖析以非同步進行。因此,有用到在 HTML 碼中宣告建立之 dijit 的程式碼,都要放在 ready() 回呼函式中執行;否則,程式碼在執行時,用到的 dijit 可能還沒有建立完畢,因而造成錯誤。
第 35 行的 ready() 內回呼函式中所包含的程式碼,只會在 HTML 碼剖析完畢,而且所有的 djijt 都建立之後,才會執行。ready() 函式可以多次呼叫,第一個參數的數值決定呼叫的先後次序,數值愈小的愈早執行。