2016年4月26日 星期二

jQuery Widget Factory - Part 1

Widget Factory - Part 1

簡介

在 jQuery UI 中要撰寫一個視覺元件 (widget) 的模組, 在 jQuery UI v1.8 之前只能使用 plugin,自 jQuery UI v1.8 版之後則可以使用 Widget Factory 模組。該模組所提供的 APIs 在後繼的 v1.9, v1.10, v1.11 版都有少許的變更。這系列文章採用的是 v1.11 版的 APIs,這是寫文章時可供下載最新、最穩定的版本。使用 Widget Factory 時會需要用到兩個模組:core.js 和 widget.js;這兩個模組都存在於 jquery-ui.js 套件之下。所以必須載入 jquery-ui.js 或 jquery-ui.min.js。如果不會用到其它 jQuery 的介面元件,也不想載入整個 jquery-ui 的套件,就得將這兩個模組從 jquery-ui 套件中分離出來後,單獨載入。

這篇文章只解釋 Widget Factory 最重要的基礎部份,也就是只宣告一個 widget,完全沒有實作任何東西時,所宣告的 widget 會長成什麼樣。

使用 Widget Factory 宣告一個 widget 時所用的語法如下:

$.widget(name, [base], prototype);

name
字串值。為此 widget 所取的名字,名字必須包含命名空間,例如: myNameSpace.myWidget。命名空間只能有一層,撰寫應用程式時,基本上是用不到這個命名空間的,但一定得給;因為 jquery-ui 會在 $ (這個 "$" 是 jQuery 在 script 中的簡寫)之中建立一個名為 myNameSapce 的物件,而在 $.myNameSpace 之下,又有個名為 myWidget 的物件,這就是宣告後 widget 所在的位置。所有自訂的 widget,只要命名空間相同,都會被放在一起;這樣比較好管理。同一個名命空間下的 widget 名稱就不能重複了。
base
選項參數。此為所宣告之 widget 要繼承之模組名稱。例如: $.ui.button 或 $.ui.dialog。如果此參數從缺時,預設為繼承 $.Widget(注意:這裡 Widget 的 W 是大寫字母,表示它是一個物件類別的名稱)模組。
prototype
賦予值為一函式 (function),在此函式中必須實作所有的繼承自 $.widget 的函式,這些函式包含供內部使用的函式 (private method),及供外部呼叫的函式 (public method)。供內部使用的函式,依慣例,在函式名稱都前置一個底線 "_",如: _create();供外部呼叫的函式則無前置底線,如:enable()。這種命名方式並非強制的規則,只是一般通用的慣例。

this.options & this.element

當執行完 $.widget() 之後,widget 的基本架構就構建完成了。此時,widget 己具有兩個很重要的屬性:options 及 element。在實作 widget 的各函式時,常會使用 this.options 及 this.element 去參考這兩個屬性。

options
是個 Javascript 的物件,起始時預設內含兩個屬性:disabled 和 create。將來 widget 中要包含其它的屬性時,也都會放到這個物件裡。
disabled
預設值為 false。當呼叫 .disable() 時,此屬性值會被改成 true。程式中可以偵測此屬性值以決定 widget 的狀態。
create
預設值為 null。傳入值為一回呼函式 (callback function),當 widget 具體化 (instantiate) 時,會觸發一個型態為 create 的事件,同時將此回呼函式當成事件的處理函式執行。
element
目前使用到的 jQuery 物件。在 jQuery 中,函式常常依序針對數個 jQuery 物件逐一處理,而 this.element 之中只含了一個 jQuery 的物件,也就是當前正在處理的物件。

Public Methods

假如宣告了一個名為 myWidget 的 widget。要呼叫 myWidget 中之函式的方式為 myWidget(methodName),methodName 是函式名稱的字串。這種方式是傳統 jQuery 呼叫函式的方式。在 v1.11 版後,還可以用 myWidget.("instance").methodName() 的方式來呼叫。以下是定義完 widget 之後,在還沒有實作任何函式時,就可以呼叫而不會抛出錯誤訊息的函式,因為完全沒實作,所以也做不了什麼事。各函式細節的部份,等實作時再做說明。

myWidget.("option", optionName)
myWidget.("option", optionName, value)
myWidget.("option")
myWidget.("option", object)
第一個呼叫方式讀取 optionName 的值,optionName 需為字串值。第二個呼叫方式設定 optionName 的值為 value。第三個呼叫方式讀取所有 option 的值,傳回值為一個物件,內含所有的 option 及其現值。第四個呼叫方式一次設定數個 option 的值,傳入值為一個物件,物件中所含有的 option,其值都會被重新設定為物件中的值。
myWidget("widget")
傳回 jQuery 物件。這個函式不需要實作,就這樣用,其實這個函式只是把 this.element 傳回來而已。
myWidget("instance")
傳回 widget 的 instance。這是唯一不能用 myWidget("instance").instance() 方式來呼叫的函式,這樣呼叫也沒什麼意義。這個函式也不需要實作,它主要的作用就在提供另一種呼叫 widget 中其它公用函式的方法罷了。
myWidget("disable")
Disable widget。此時會在 widget 的屬性 class 中加入 nameSpace-widgetName-disabled 這個字串。字串中的 nameSpace 及 widgetName 由 widget 的 nameSpace 及 widgetName 的字串值來取代。然後把 options.disabled 的值改成 true。
myWidget("enable")
Enable widget。將 widget 之 class 中的 nameSpace-widgetName-disabled 字串刪除;然後把 options.disabled 的值改成 false。
myWidget("destroy")
清除 widget。

範例

範例中宣告了一個名為 prod.item 的 widget。並呼叫上述的各函式,大多數的結果都要到「開發者工具中」的主控台去看。只有 enable() 及 disable() 這兩個函式定義了 CSS 的類別,可以展示一點視覺效果。網頁下載後,過 2 秒會 disable widget,再過 2 秒會 enable widget,再過 3 秒會 destroy widget,因為只宣告了 widget 也沒實作,也沒什麼可以 destroy 的。

head





在 <head> 中記得要載入 jquery-ui.min.js。

CSS

<style>
html {
    width: 100%;
    height: auto;
}
body {
    margin: 0 auto;
    width: 1000px;
    padding: 20px;
}
#item {
    width: 200px;
    height: 200px;
    border: 2px solid gray;
    background-color: skyblue;
}
.prod-item-disabled {
    opacity:0.3;
}
</style>

CSS 中用 #item{} 定義了元件的基本外觀。
.prod-item-disabled{} 定義了當元件被 disabled 時,將元件的 opacity 值降到 0.3。這樣在執行時會造成一點視覺效果。

HTML

<body>
    
</body>

HTML 中定義了一個 id = "item" 的元件,作為 widget 的掛載點。

Script

<script>
// widget created by widget factory, should put into another file
(function($, undefined) {
    $.widget( "prod.item", {
  
    });
})(jQuery);

 
$(function(){
    console.log("jQuery version is " + $().jquery);
    console.log("jQuery-ui version is " + $.ui.version);

    console.log($.prod);

    var elem = $("#item").item();
 
    console.log(elem.item("option"));
    console.log(elem.item("widget"));
    console.log(elem.item("instance"));

    setTimeout(function(){
        elem.item("disable");
        console.log(elem.item("option", "disabled")); // true
    }, 2000);
    setTimeout(function(){
        elem.item("enable");
        console.log(elem.item("option", "disabled")); // false
    }, 4000);
    setTimeout(function(){
        elem.item("destroy");
    }, 6000); 
});
</script>

第 3~7 行,定義了一名為 prod.item 的 widget。一般來說,這一部份的程式碼在實務上實作時,都會分開放在另一個檔案之中,以便維護。
第 11~12 行,在主控台中列印出使用的 jQuery 及 jQuery-ui 的版本。
第 14 行,在主控台中列印 $.prod 物件,所有以 prod 做為命名空間的 widget 都會放在這個物件之中。
第 18~20 行,依序呼叫三個函式,並在主控台印出傳回值。 可以比較一下 widget() 和 instance() 兩個函式的傳回值有何不同。
第 22~25 行,設定一個定時器,在網頁下載 2 秒過後,disable widget ,並將 options.disabled 的值印在主控台中。
第 26~29 行,設定一個定時器,在網頁下載 4 秒過後,enable widget ,並將 options.disabled 的值印在主控台中。
第 30~32 行,設定一個定時器,在網頁下載 6 秒過後,清除 widget。這個只是呼叫了 destroy() 這個函式,完全沒做任何事。

沒有留言:

張貼留言