文章目錄
- 第一步是為自訂小組件建立必要的目錄結構
- 第二步 建立展示單個作者的HTML片段
- 第三步 把HTML片段變成Dijit模板
- 第四步 使用dojo.declare建立小組件的類
- 第五步: 美化
在這個教程中,我們將會示範如何利用Dojo 和Dijit架構來建立自訂的小組件。 主要會使用到dijit._Widget 和dijit._Templated 基類和mixin。
對dijit架構的基礎知識,可以參看前兩篇教程
難度:中等
適用Dojo版本: 1.6
作者:Brian Arnold
Brian Arnold is a software engineer at SitePen, Inc. He has a lovely wife, two cute dogs, is an active member of (and presenter at) Webuquerque, and ranks among the top 3% of fake guitarists in Rock Band.
原文串連:http://dojotoolkit.org/documentation/tutorials/1.6/recipes/custom_widget/
譯者 :feijia tiimfei@gmail.com
Dojo的Dijit 庫包含了豐富的介面小組件(Widgets),通過使用這些小組件,可以打造出強大的Web應用介面,從進階的表單元素,到複雜頁面配置。
但是對於一些較複雜的應用,開發人員仍會碰到做進一步定製的需求,例如非常複雜的資訊展示需求。你當然可以手動的自己構造DOM來展示資料,但是如果利用Dijit已經提供的架構和工具,我們可以快速的開發出靈活又強大的自訂小組件。
情境
假設我們需要開發一個能展示所有Dojo教程作者的簡介資訊的頁面。我們手頭的資料來源是如下的JSON資料:
[ { "name": "Brian Arnold", "avatar": "/includes/authors/brian_arnold/avatar.jpg", "bio": "Brian Arnold is a software engineer at SitePen, Inc., ..." }, /* More authors here... */]
我們的需求是把這些資訊以下面的DOM結構展示在頁面上:
<body> <!-- Headers and whatnot --> <h2>Authors</h2> <div id="authorContainer"> <!-- Authors go here! --> </div></body>
當然,我們希望這個展示頁面可以再加點效果,例如當滑鼠移到某個作者上時,背景色可以淡入顯示。
最終展示的頁面效果如下:
解決方案
我們可以建立一個自訂的Dojo小組件來實現這一需求,分如下幾個步驟:
1. 建立必要的小組件目錄結構
2. 建立展示單個作者的HTML標記
3. 在#2的基礎上把這些標記變成Dijit模板
4. 使用Dojo.declare 來建立我們的小組件類
5. 進行必要的CSS美化
第一步是為自訂小組件建立必要的目錄結構
雖然這一步看起來有些多餘,但是為你開發的自訂小組件安排一個合理的目錄結構是一種良好的編程習慣的。在這裡我會建立一個名為custom的目錄,作為存放我所有代碼和模板的命名空間。當然目錄的名稱完全取決於你自己,可以是你就職的機構的名稱。 然後我為即將建立的自訂小組件取名叫AuthorWidget,所以我會建立一個AuthorWidget目錄,這個目錄會包含與這個小組件相關的css和html模板以及原始碼。 最終的檔案結構會如所示:
第二步 建立展示單個作者的HTML片段
作為第一個自訂小組件,我們會試著從最簡單的做起。它僅僅在頁面上展示一些資料。
這裡我們使用如下的簡單片段來展示一個作者的資訊。最外層是一個容器div,記住建立Dijit模板時一定要有一個唯一的根節點。這裡這個容器div就是我們模板的根節點。然後我們使用一個H3標籤來展示作者名字,一個img標籤來展示頭像,一個p標籤來展示作者的簡介。
<div> <h3>Brian Arnold</h3> <img src="/includes/authors/brian_arnold/avatar.jpg"> <p>Brian Arnold is a software engineer at SitePen, Inc., ...</p></div>
第三步 把HTML片段變成Dijit模板
當使用Dijit._Templated (我們小組件的基類)時,你對模板會有許多操作的方式:
1. 你可以在模板中自動插入變數值
2. 你自己在模板中定義附著點元素(attach point),這樣你在widget中引用並編程操縱這些DOM元素
3. 可以在模板中的元素上設定DOM事件的處理函數
在我們這個例子裡,我們主要會用到自動的變數值插入。現在我們來建立檔案: custom/AuthorWidget/templates/AuthorWidget.html . 檔案的內容基本和第二步中的HTML片段相似,只是會添加一些Dijit模板特有的屬性。
<div> <h3 data-dojo-attach-point="nameNode">${name}</h3> <img class="${baseClass}Avatar" src="" data-dojo-attach-point="avatarNode"> <p data-dojo-attach-point="bioNode">${!bio}</p></div>
在這一模板中:
1. 我們使用 ${attribute}的文法來直接插入一些值,例如這裡我們插入了作者姓名${name}
2. 類似的,還有一種文法是${!attribute}. 這兩者的區別是,${!attribute} 會對值原樣插入而不做轉義。在這裡我們在${!bio}位置要替換的值會包含一些HTML標記,我們不希望Dijit._templated 對這些標記做轉義。
3. 所有基於dijit._Widget的小組件都有一個baseClass屬性。
4. 在上述模板中,我為每個節點都定義了附著點屬性,這樣在My Code中就可以直接使用諸如myAuthor.nameNode 這樣的代碼直接存取到H3節點。
你也許已經注意到了我在模板中的img標籤裡沒有指定src屬性值。 那麼如果我們的資料裡某個作者沒有包含頭像圖片怎麼辦?我們需要能夠在建立我們的小組件時自動設定處理這類情形的預設值。 後面的步驟會進一步解釋如何解決這個問題。
第四步 使用dojo.declare建立小組件的類
這一步我們要在custom目錄裡建立AuthorWidget.js 。 並且我們添加一個預設的頭像的圖片檔案。
建立之後的目錄和檔案結構如下:
在AuthorWidget.js 裡我們將使用dojo.declare 建立我們的小組件類。 如下:
// custom.AuthorWidgetdojo.provide("custom.AuthorWidget"); // 聲明依賴的模組和基類dojo.require("dijit._Widget");dojo.require("dijit._Templated"); // Create our widget!dojo.declare("custom.AuthorWidget", [dijit._Widget, dijit._Templated], { /* 我們的自訂小組件屬性將會被添加在這裡 */}) // and that's it!
這段代碼中:
1. 我們使用dojo.provide 來聲明我們這個js檔案提供了一個名為custom.AuthorWidget 的資源。 從1.6開始, Dojo加入了對非同步模組載入機制的支援(AMD),但是為了簡單起見,這裡我們沿用了傳統的Dojo模組聲明和載入系統(dojo.provide 和dojo.require)。
2. 我們使用了dojo.require 來引用我們的小組件所依賴的模組 (dijit._Widget 和dijit._Templated 這兩個基類)
3.我們使用dojo.declare 聲明了一個類, custom.AuthorWidget,該類使用dijit._Widget 和dijit._Templated作為基類。
注意在這個js檔案中,我們不需要使用dojo.ready ---- 這是因為我們在開發一個dojo的模組。 而Dojo的模組載入系統會保證你所依賴的模組都載入成功後再運行載入後續的模組。
當然,上面的代碼還只是一個空架子,要讓它真的可以工作,我們還需要給我們的小組件設定一系列屬性。 下面是加上了屬性設定的dojo.declare的程式碼片段。
dojo.declare("custom.AuthorWidget", [dijit._Widget, dijit._Templated], { // 設定一些預設值 // These typically map to whatever you're handing into the constructor name: "No Name", // Using dojo.moduleUrl, we can get a path to our AuthorWidget's space // and we want to have a default avatar, just in case avatar: dojo.moduleUrl("custom.AuthorWidget", "images/defaultAvatar.png"), bio: "", // 載入我們的模板 - important! templateString: dojo.cache("custom.AuthorWidget", "templates/AuthorWidget.html"), // 將會被應用到模板根節點的css類名 baseClass: "authorWidget", // 指向我們背景動畫對象的引用 mouseAnim: null, // 用於背景的顏色屬性 baseBackgroundColor: "#fff", mouseBackgroundColor: "#def"});
首先我們定義了一些屬性用於儲存作者的基本資料:姓名,簡介和頭像。我們也提供了這些屬性的預設值。 在設定頭像的預設值時,我們使用了dojo.moduleURL來引用當前小組件所在的路徑,並訪問該路徑下的圖片檔案夾。
通過使用templateString屬性,和dojo.cache 我們載入了之前定義的模板檔案
設定baseClass屬性,該屬性會被設定為小組件的DOM根節點的CSS類。 在這裡就是我們模板中的最外層div節點。
我們還留出了用於設定動畫的屬性和背景色的屬性。
到目前為止,我們的小組件已經初具雛形,實際上它已經可以運行並顯示一些簡單的資訊了。 當然我們還需要進一步完善它。
接下來我們會添加postCreate方法,一個定製的頭像設定方法,已經一個輔助方法用來變換背景顏色。
postCreate方法是用來添加我們的工作邏輯的主要入口。它的調用時機是在小組件的DOM結構成功建立後,但是還沒有被添加到頁面的DOM樹之前(也即使用者看不到這個小組件的DOM節點)。因此通常我們把初始化的工作放在這個方法中。
postCreate: function(){ // Get a DOM node reference for the root of our widget var domNode = this.domNode; // Run any parent postCreate processes - can be done at any point this.inherited(arguments); // Set our DOM node's background color to white - // smoothes out the mouseenter/leave event animations dojo.style(domNode, "backgroundColor", this.baseBackgroundColor); // Set up our mouseenter/leave events - using dijit._Widget's connect // means that our callback will execute with `this` set to our widget this.connect(domNode, "onmouseenter", function(e) { this._changeBackground(this.mouseBackgroundColor); }); this.connect(domNode, "onmouseleave", function(e) { this._changeBackground(this.baseBackgroundColor); });}
在postCreate裡我們利用baseBackgroundColor屬性設定了domNode的背景色,並且設定了onmouseenter和onmouseleave的事件處理函數,因此當滑鼠移至上方在我們的DOM節點上時,_changeBackground 函數就會被調用。 下面我們來看看這個函數:
_changeBackground: function(toCol) { // If we have an animation, stop it if (this.mouseAnim) { this.mouseAnim.stop(); } // Set up the new animation this.mouseAnim = dojo.animateProperty({ node: this.domNode, properties: { backgroundColor: toCol }, onEnd: dojo.hitch(this, function() { // Clean up our mouseAnim property this.mouseAnim = null; }) }).play();}
註: 為什麼我們要在這個方法名前加底線呢?這是一種dojo的命名規範,在方法和對象屬性前加底線表示該方法或對象是類內部成員,不應被使用者直接使用。雖然JavaScript語言沒有強制禁止這類調用,但是這是一個良好的編程習慣,也是一種提示使用者正確使用API的方法。
在這個方法中,我們先檢查是否當前有動畫正在執行,如果有的話我們先停止它。 然後在設定新的動畫並儲存在mouseAnim中,並開始播放。 這個例子十分類似於我們在Dojo動畫教程http://dojotoolkit.org/documentation/tutorials/1.6/animation/ 中的效果,只不過顏色有所差別。
最後,我們需要解決一個問題,就是如果某個作者沒有提供頭像圖片,我們需要給他設定一個預設的頭像。 這裡我們通過建立一個定製的屬性設定方法(custom setter) 來實現。 這類方法有固定的命名規則, _setXXXAttr 其中XXX是你要設定的屬性的名稱(首字母需大寫)。 例如我們這裡要設定的是“avatar屬性,因此我們需要的方法名是_setAvatarAttr
在小組件被建立或者使用者使用屬性設定方法:myWidget.set("avatar",somePath) 時,定製屬性方法就會被調用.
_setAvatarAttr: function(av) { // We only want to set it if it's a non-empty string if (av != "") { // Save it on our widget instance - note that // we're using _set, to support anyone using // our widget's Watch functionality, to watch values change this._set("avatar", av); // Using our avatarNode attach point, set its src value this.avatarNode.src = av; }}
這個方法主要是做一個安全檢查,在使用者傳入Null 字元串時,就使用預設的頭像。
從Dojo1.6開始,所有基於dijit._Widget的小組件都會自動繼承dojo.Stateful ,這個類加入了對類屬性的變化的監控。我們在_setAvatarAttr 中使用this._set() 方法就是為了遵循dojo.Stateful的規則,保證所有屬性的變化可以被檢測到。
所有這些都完成後,我們現在有了一個可以工作的小組件。 雖然看起來還不是那麼美觀。
查看樣本
第五步: 美化
使用Dijit._Widget的好處之一,就是他提供了一個baseClass屬性作為根節點的CSS樣式類。之前建立的目錄結構中有專門的css目錄,接下來我們在css目錄中建立一個AuthorWidget.css 檔案。
/* AuthorWidget.css */.authorWidget { border: 1px solid black; width: 400px; padding: 10px; overflow: hidden; /* I hear this helps clear floats inside */} .authorWidget h3 { font-size: 1.5em; font-style: italic; text-align: center; margin: 0px;} .authorWidgetAvatar { float: left; margin: 4px 12px 6px 0px; max-width: 75px; max-height: 75px;}
可以看到我們定義了authorWidget 類,這個類是baseClass所制定的,它會被應用到模板的div根節點上。 同時,模板中還有 ${baseClass}Avatar類,因此我們定義了authorWidgetAvatar類。(這隻是一些最基本的美化,別對我要求太高了,我不是一個設計師)
最後一步在頁面的header部分加入我們的css檔案,我們就有了一個美觀的作者列表小組件啦!
查看樣本
小結
這個教程裡,我們看到使用dijit._Widget 和 dijit._Templated 作為基類,建立一個自訂的小組件很簡單。 我們可以快速的建立模板,添加自訂邏輯,並且定義自己的css來美化小組件的外觀。
雖然這個例子很簡單,但是你要知道絕大多數Dijit裡的小組件都是基於這兩個基類開發的,使用這些工具可以開發出強大的介面組件。下面的參考閱讀中列出了一些很好的文檔可供你參考。
* dijit._Widget on the Dojo Reference Guide
* dijit._Templated on the Dojo Reference Guide
* Tutorial on dojo.declare
* Dojo Reference Guide: Writing Your Own Widget