“MakeNode”部件擴展
7月8日下午02:11,在2011年由薩蒂揚| 開發 | 6評論在我以前的文章中, 一個YUI 3應用配方 ,我表明方式使用Y.substitute一個非常基本的模板處理器。 從那裡生活的想法了,從#YUI IRC頻道鄉親的建議,我是我的網站上提供一個Widget擴展,稱為MakeNode 。 MakeNode是不是一個通用的模板處理器,它不意味著為一體。 另一方面,它緊密集成與YUI 控件的基礎類,包括ClassName和事件傭工和國際化。 在這篇文章中,我將採取微調的例子,並修改它遵循從我以前的文章的指引和使用MakeNode。 修改後的微調組件( JS , CSS , 精靈 ),以及一個例子是從我的網站。 在本文末尾可以找到更多的資源鏈接。
擴展你的組件
你一旦MakeNode加載,需要包括在你的模塊YUI().use()語句使用名稱'makenode' 然後,延長你的widget,你列出的第三個參數Y.Base.create()像這樣:
Y.Spinner = Y.Base.create( “微調”, Y.Widget, [Y.MakeNode] { / /實例成員... }, { / /靜態成員 } );
您可以添加任何合適的擴展部件,如WidgetParent,WidgetChild,WidgetStdMode,沿MakeNode等MakeNode增加了兩個受保護的方法, _makeNode和_locateNodes,它會讀取從幾個靜態屬性,如果找到。
這個擴展的所有成員是protected或private,因為他們是為了組件的開發和使用這些組件的實施者,誰不應該與他們的困擾。
定義模板
你通常會做的第一件事是定義為您的組件的模板。 對於微調,我們的模板將是:
_template: <input type="text" title="{s input}" class="{c input}">“ “<button type="button" title="{s up}" class="{c up}"> </按鈕>” “<button type="button" title="{s down}" class="{c down}"> </按鈕>” 。加入('\ N'),
通常會被命名為默認模板_TEMPLATE和沿線的其他類的靜態屬性,如宣布ATTRS 。 如果沒有其他明文規定,MakeNode將使用此模板。 模板是純HTML加上一系列括在大括號中的佔位符,每個單個字符(加工代碼)和一個或多個參數。 佔位符和他們生產什麼是:
{@ attributeName}配置屬性的值{p propertyName}實例的屬性值{m methodName arg1 arg2 ….}給定的方法的返回值。 後跟由空格隔開的參數的方法名稱和任何數量的加工代碼。 必須括在雙引號字符串。 數字,布爾值和null字符串將被轉換成適當的數據類型{c classNameKey}CSS類名從產生_CLASS_NAMES靜態屬性{s key}字符串從strings屬性,子屬性使用key。{? other placeholder }生成的字符串checked時,其餘的佔位符處理的結果是真實的。{}任何其他值將處理就像Y.substitute不。
例如, {@ value}將轉化到this.get('value') {p value}轉換為this['value']
{m}佔位符是稍微複雜一些。 m加工代碼後的第一個參數是該方法的名稱和參數,將通過給定的方法全部由空格隔開的休息。 這些參數可以是數字, null , true , false或字符串括在雙引號。 MakeNode將解析和轉換他們到正確的類型,從而{m myMethod 123.45 true “this is a string”}將導致在調用this.myMethod(123.45, true, “this is a string”)前兩個參數其正確的數據類型轉換為字符串可以包含空格。 包括一個雙引號,使用\\"正因為JavaScript解釋一個單一的一個丟棄它之前,它得到到MakeNode。只允許使用雙引號,MakeNode不eval() eval eval()這樣的解析器是有限的雙反斜線但安全。什麼,但號碼, null ,布爾和雙引號字符串將被忽略。
{?}佔位符是很方便的使用複選框和單選按鈕。 它會產生字符串“checked”根據真值的處理指令的代碼如下。 因此, <input type=”checkbox” {? m getLength}/> <input type=”checkbox” {? m getLength}/>將產生顯著的複選框,如果getLength方法返回什麼,但零。 {?}將接受任何其他佔位符,但它不僅使前三感。
為{c}佔位符,我們需要定義有1 _CLASS_NAMES財產。
進一步佔位符,可以添加到MakeNode加入到他們_templateHandlers哈希。
_CLASS_NAMES財產
沿著與ATTRS和_TEMPLATE靜態屬性,我們可以定義一個_CLASS_NAMES屬性,它指向一個字符串數組。 這些字符串都將被用來生成一個類的。 因此_CLASS_NAMES: ['input']會產生的className “yui3-spinner-input” 。 那些類名存儲實例屬性this._classNames 。 {c input} “yui3-spinner-input” {c input}上面的模板中的佔位符將被替換。
你可以使用_CLASS_NAMES屬性來生成任意數量的類名,你是否在模板或不使用他們。 你仍然可以達到從內部this._classNames這些額外的類名。 使用yui3前綴NAME的靜態屬性的價值,產生的className轉為小寫,然後在給定的的字符串_CLASS_NAMES (這一次將不會變成小寫),全部由連字符分隔。 _classNames哈希也將包含為boundingBox類名和的contentBox ,下的第一個"."鍵,並根據第二個“content”鍵。 部件分配到boundingBox NAME在繼承鏈中的每個類的屬性的值派生的類名,開始yui3-widget 。 MakeNode存儲到this._classNames只有最頂級的className boundingBox 。
如果一個組件是幾個層次,從Widget一樣, SuperSpecialSpinner從的繼承SuperSpinner從繼承從繼承Spinner Widget, _CLASS_NAMES this._classNames Spinner Widget,如果其中的任何或全部定義_CLASS_NAMES屬性,MakeNode將產生所有的類名,並存儲在他們this._classNames 。 你不必包括在每個級別的名字已經宣布,在以前的水平。 事實上,它是更好的,你不會因為在每個級別的類名,使用該級別的屬性值的NAME 。 因此, SuperSpecialSpinner , {c input}仍將導致的“yui3-spinner-input”和“yui3-superspecialspinner-input” ,所以它會保持你的CSS文件仍然有效。
{}佔位符
構件有strings配置屬性定義,雖然它不與任何值初始化。 此屬性是為了保存字符串(或通過屏幕閱讀器,閱讀)的用戶是可見的。 重要的是,你永遠不包括直接在模板中可見字符串。 這是不是的MakeNode的要求 - 在所有它從來就不是一個好主意。 應始終被視為或讀取用戶的所有字符串放置在strings屬性。 strings屬性包含了每一個人的文字是由位於其關鍵的哈希。 微調器組件有以下字符串,你可以看到在上面的模板使用。
字符串:{ 值:{ 輸入:“按箭頭/向下鍵輕微的增量,頁/主要增量。” 了起來:“增量” 下來:“減” } },
這樣做的最好的部分是組件可以使用您的組件的開發本地化為其他語言很容易。 當創建一個微調的實例,你可以這樣做:
mySpinner =新的微調({字符串:Y.Intl.get('微調')}); 以這種方式設置的配置屬性strings取代與使用先前定義的語言,從語言資源文件的默認strings值。 {s}佔位訪問存儲在字符串strings屬性,無論是默認的或翻譯的,如果設置。 {s xxxx}佔位符,在事實上,沒有什麼比一個快捷方式到更多的{@ strings.xxxx}佔位符。 然而,第一次只能在頂層訪問字符串的同時,例如, {@ strings.xxxx.yyyy.zzzz}將允許您訪問字符串更深。
使用在renderUI _makeNode
我們使用模板來創建我們的組件的標記。 這樣做,我們可以調用MakeNode的_makeNode方法,是這樣的:
renderUI:函數(){ 。this.get(“contentBox)追加(this._makeNode()); },
這將填補我們的部件在contentBox處理模板的標記。 _makeNode方法返回一個實例Y.Node可追加或插入任何地方,或只是為以後使用而舉行。 它不返回一個字符串,它產生一個Node實例。
_makeNode方法有兩個可選參數:一個參考模板和對象,以填補在佔位符,,作為Y.substitute 。 在我們簡單的微調例如,有一個單一模板的整體部件,但其他部件可能需要位和幾個模板件。 在這種情況下,你通常會致電_makeNode的主要部分沒有參數,調用不同的模板,再次填補多餘的部分。 例如包含此renderUI方法:
renderUI:函數(){ this._makeNode的fieldset =(); this.each(函數(項目){ fieldset.appendChild(this._makeNode(MultipleTemplates.RADIO_TEMPLATE項)); }); 中this.get(“contentBox)追加(字段集); }
_makeNode第一次調用返回一個Node實例存儲在變量中fieldset 。 樣品組件也延伸與Y.ArrayList所以RADIO_TEMPLATE將被從存儲在數組列表中的項目和所產生的附加 的節點值填充fieldset前最後追加到contentBox 。 如特殊的佔位符{@} {p}仍將主要對象的屬性或屬性。 正如Y.substitute將嵌套的項目將被處理。
_locateNodes方法
MakeNode進一步提供_locateNodes方法,將盡力找到所有的元素在聲明的類名_CLASS_NAMES 。 找到特定的元素,您可以通過任意數量的className鍵,否則, _locateNodes嘗試他們所有。 對於每個發現的className的每個元素中, _locateNodes會產生一個私人的實例的屬性,使用下劃線前綴鍵的名稱和“Node”後綴。 因此,我們飛旋例如, _locateNodes會生成的屬性_inputNode , _upNode和_downNode 。 如果有多個元素具有相同的className, _locateNodes將返回一個引用到他們的第一。 如果一個元素沒有被發現,沒有變量將被創建。
在微調組件,我們使用_locateNodes後創建的標記:
renderUI:函數(){ this.get(CBX公司)追加(this._makeNode()); this._locateNodes(); },
_EVENTS的靜態屬性
一個進一步的屬性可以被定義沿線_TEMPLATE和_CLASS_NAMES ,是_EVENTS 。 _EVENTS將包含類名鍵,每個包含一個事件的類型和方法來處理它們的哈希散列。 這是更好地用一個例子來解釋:
_EVENTS:{ '。':{ 關鍵:{ FN:“_onDirectionKey” 參數:(Y.UA.opera“下來:”:!“記者:”)+“38,40,33,34” }, 的mousedown:'_onMouseDown' }, “......”:{ 的MouseUp:'_onDocMouseUp' }, 輸入:{ 變化:“_onInputChange”, } },
_EVENTS是一個具有任意數量的屬性的對象(哈希)。 屬性的名稱,即哈希鍵,確定元素的事件,我們會聽取。 他們是在使用相同的標識符_CLASS_NAMES 。 有兩個額外的特殊功能鍵"."和".." 雖然內嵌套元素contentBox的className鍵, "."關鍵指的boundingBox本身,而".."是指包含此部件的文件。 認為他們做chdir命令時,在位於boundingBox水平。 _EVENTS財產處理後renderUI , bindUI和syncUI方法,被稱為widget的預期,因此已經插入文檔正文內,否則".."將失敗。
條目中的每個_EVENTS是1進一步對象事件的類型作為其關鍵和1實例的方法來處理它的名稱。 _EVENTS ,是一個靜態變量,有沒有來訪問this所以它可以不採取實際功能的引用,只有名稱的事件偵聽器方法。 一些事件類型,如需要額外的參數, key事件。 在這種情況下,而不是提供的事件處理程序的名稱,你可以提供與物業fn args args對象舉行的函數的名稱和額外的參數,需要時。
MakeNode將使用Node.delegate聽嵌套元素的事件,而它會使用Y.on聽從事件boundingBox和筆體。 (注:聽任何嵌套元素的key事件僅適用於版本3.4.0pr1及以上,由於該代表團key 。事件是不是之前的所有其他功能的工作以及與以前的版本)
_EVENTS聲明是累積性的,當組件從一個繼承。 繼承鏈中的每個類都會有其自己的_EVENTS聲明分開處理。
_ATTRS_2_UI的靜態屬性
活動是雙向的,從UI組件從組件到UI。 首先是處理由_EVENTS屬性。 然後有由屬性值的變化,必須體現在用戶界面中引發的事件。 正如我在前面的文章,有任何改變配置屬性的輔助療效時提到,他們應改變事件監聽器處理,而不是可選的setter方法的屬性,它應該只處理正常化設置的值。 UI應該反映配置屬性的狀態,首先在syncUI時被初始化,然後對每一個屬性更改事件。 對於後者,我們需要附加一個事件監聽器,我們在做bindUI 。 部件已經提供了一種機制,使這個簡單,我在上一篇文章的評論。
部件使用,它包含一個與另外兩個性質,對象_UI_ATTRS SYNC和BIND實例屬性_UI_ATTRS 。 這些是一個數組,列出最初同步時的配置屬性的名稱,然後聽取以反映當前值保持UI。 部件預計每個條目有一個與之關聯的方法,由前綴屬性名後的第一個字符轉換為大寫有駱駝的情況下適當的方法名稱的屬性名稱的命名_uiSet 。 因此,如果"value" 在任何上市_UI_ATTRS陣列( SYNC或BIND )的Widget希望找到一個_uiSetValue方法。 這種方法將收到兩個參數的value設置和src的變化。 這是為我們的微調代碼_uiSetValue方法:
_uiSetValue:功能(價值,SRC){ 如果(SRC ===界面){ 返回; } this._inputNode.set(值,this.get(格式化)(值)); },
你在這段代碼中看到對應的字符串常量的所有大寫字母標識符宣布在其他地方,讓YUI壓縮到更好地完成其工作。 ,基本上,該方法設置的value在HTML屬性<input>中的新值集,被格式化後。 被由_locateNodes提供參考文本框。 src參數的初步檢查,看看是否設置為字符串值'ui' 如果是這樣的,將採取任何行動。 這是為了避免無休止的循環。 如果用戶在輸入框中輸入的東西,其價值將進入value的配置屬性,然後將火的事件valueChange ,會得到_uiSetValue呼籲,如果不加以控制,然後去改變輸入框的值,將再次引發整個過程。 因此, _uiSetValue ,如果我們知道的變化,從UI,我們什麼也不做,所以打破循環。 然而,這需要另一塊代碼的其他地方。 在DOM事件監聽器,當我們設置的配置屬性,我們使用的第三個可選參數設置,像這樣:
_afterValueChange:功能(EV){ this.set(值,ev.newVal {SRC:用戶界面}); }
這是我們確保,因此,從用戶界面的變化標記,然後檢查相同的標誌,以避免環路。
這一切說,我還沒有提到_ATTRS_2_UI在本節的標題提到的靜態屬性。 正如我在以前的文章顯示評論(通過我在他們的失誤),確保正確列出的所有屬性影響的UI是有些凌亂。 你永遠不應該初始化_UI_ATTRS以來從頭部件已經列出了一大堆的屬性和那些會丟失。 你有新的屬性名稱串連在現有的,這有點很難記得如何做是正確的。 為了簡單,MakeNode將讀取靜態屬性_ATTRS_2_UI並為你做什麼,串聯。 它將連接所有這些清單,以便從繼承鏈中的每一類,每一級,每一類可以處理自己的屬性。 在微調,我們有:
_ATTRS_2_UI:{ BIND的:價值, 同步:VALUE },
MakeNode將接受的名稱數組或一個單一的屬性名稱,在這種情況下。
自然產生的問題,為什麼兩份名單,結合其他同步? SYNC陣列往往比少BIND列表項和,這是因為組件的模板可能已經有很相同的配置屬性的默認值,有沒有必要做一個初步的同步。 因此,如果的value配置屬性的默認值是一個空字符串的<input>在模板中的元素沒有value屬性,那麼有沒有必要在初始化同步。
MakeNode將檢查這些陣列中的任何重複的條目。 如果任何出現的,它意味著已經處理此屬性和任何新的聲明將最有可能超越我們的組件1類繼承_uiSetXxxx它的處理程序。 順便說一下,MakeNode重複的條目,在_CLASS_NAMES ,這也可能導致在一些衝突,但並非所有的情況下,還檢查。 MakeNode會寫消息到任何這樣的錯誤日誌。
結論
MakeNode提供了一個非常簡單的模板處理器,簡單的功能,高度集成與Widget的基礎類。 它還提供了創建在模板中使用的類名,並使用這些名稱來定位節點的輔助方法。 它還提供了掛接到由UI組件本身所產生的事件和與之相關的每一個方法的手段。 它所有這些東西,同時注意尊重直線上升Widget和任何級別的類,你可以定義繼承鏈。
它不提供絕對的所有可能性,但涵蓋了他們良好的範圍。 然而,它並不妨礙你增加額外的功能。 你可能很少有寫bindUI或syncUI方法,如果你使用由MakeNode提供膠水,但你可以這樣做,因為MakeNode不使用它們。
作為獎勵那些有耐心去閱讀,到目前為止,我還修改安東尼Pipkin的圖庫組件的按鈕集:
API文檔可以在這裡找到。
共享和擴展: 書籤del.icio.us Digg它! | reddit!


這是Ryan的新的MVC的View組件在3.4.x兼容嗎? 它可以被用在這一框架兼容的方式呈現的標記?
安德魯·伍爾德里奇 - 7月9日,2011年#
安德魯 ,
這個擴展是建立一個輔助組件,按鈕和微調的例子表明,沒有建立整個應用,MVC框架。 這些組件可以在任何地方使用從Widget派生的任何其他組件。 在MVC框架,它會自然地使用這些組件一起普通的HTML或任何其他組件從Widget派生類繼承Y.View構建用戶界面,它是否使用MakeNode與否。
薩蒂揚 - 7月10日,2011年#
薩蒂揚,
這是非常偉大的! 我經歷了所有的痛點,你這個widget擴展解決。 看來,使用這個擴展可以消除大量的重複鍋爐板代碼結束了書面創建自定義部件時,同時對DOM和渲染,這是令人興奮地看到了如何連接的代碼和邏輯規範!
你將被添加到了YUI 3畫廊,它更多的人訪問。使用()?
像安德魯指出,有是Y.View事件和渲染一些概念上的重疊,雖然這兩個API是不同的。 這可能是值得搞清楚,如果是為這兩個API的共同地面有更多類似的(特別是與DOM事件的東西)。
從整體API的角度來看,您所做的一切保護/私營部門通過“_”(下劃線)前綴,我好奇地聽到您的想法。 我覺得,靜態屬性,如:_CLASS_NAMES`和`_EVENTS`等,可能也僅僅是:`CLASS_NAMES`,`活動`無下劃線前綴。 這可能只是我的偏好,但感覺過於保護:)
評論由埃里克Ferraiuolo - 7月12日,2011年#
埃里克 ,
感謝您的評論。 事實上,這個誕生於無聊的重複。 我也很喜歡它聲明的方式處理的程序性的東西,減少和規範,特別所有_uiSetXxxx方法所產生的組件整潔。
我不想處理與GitHub上和YUI的畫廊,所以我不會張貼有。 我不介意如果有人這麼做,但我不會做或維護。
DOM事件的事情來,右出Y.View除了我使用類名識別的元素,使整個擴展以來,以及廣泛使用,其中的鍵。 它還涉及掛鉤,所以你不需要重複那些從其他類繼承時,所有的類層次結構中的事件。
關於保護/私有成員,我與珍妮問團隊和保護的根據他的意見,我改變了以前所有市民提出的。
基本上有兩個開發者的角色,組件的創建者和組件的用戶或“實施者”珍妮提到他們。 這是更好,如果組件開發意味著類成員不弄亂API文檔的實現。 在這個意義上說,應該有許多成員,如CONTENT_TEMPLATE,renderUI,HTML_PARSER或Base.ATTRS的Widget從未公開的實現應該甚至不知道他們。
另一方面,如_uiSetTabIndex或_uiSetDisabled成員是非常正確的聲明為protected。 因此,在組件的開發模式,你應該總是有顯示保護,而作為實現者,你不應該。 這將防止重新實現的功能已經存在像重做這兩種方法已經做了代碼庫中的原始的按鈕組件,組件開發人員。
我想,自從珍妮不得不把它最多的球隊,在這方面有沒有指引,因此,我們將不得不忍受一些不一致的,在現有的組件。
薩蒂揚 - 7月12日,2011年#
更新:
我添加了一個更強大的處理代碼:“1”。 單/複數,例如文字處理,是有幫助的:{數量} {1 {數量}“單位”,“單位”}。 該字符串會產生無論是“1單位”或“123部隊”根據財產數量價值。
在上面的例子所示,佔位符現在可以互相嵌套。 因此,一個佔位符的參數可以是另一個佔位符的返回值。
我也改變了行事更像佔位符{?}:操作員。 ,而不是一個固定的文本,它可以返回任何東西,它的論點說,例如:{? {數量} {數量}“無”}。
作為一個極端的例子,這個模板:
{? {數量}“{數量} {1 {數量}”單位“,”單位“}”,“無”}
會產生文本“無”,“1單位”,“2個單位”,“3個單位”等物業數量連續值。
現在是作為方法_substitute提供的模板處理的方法。
薩蒂揚- 2011年8月13日, #
進一步的變化:
現在的_EVENTS靜態屬性,每個事件定義哈希聽眾,需要一個額外的虛擬選擇情侶。 的結構_EVENTS:
_EVENTS: {
selector: {
eventType: listener,
....
}
}
選擇是使用在_CLASS_NAMES屬性創建用於在模板中的HTML元素,這有助於找到他們的類名的鍵。
有兩個特殊的選擇:“。” 表示boundingBox的'......“ 部件所在的文件
我現在又增加了其他兩個虛擬的選擇,這一點,全部大寫,是指本身的部件和Y到Y的實例,例如:
_EVENTS: {
THIS: {
visibleChange: '_afterVisibleChange'
},
Y: {
'broadcastingWidget:somethingChange':'_afterSomethingChange'
}
}
Y鍵,雖然它是指為代表的Y實例,將採取由JavaScript字符串“Y”型。 始終使用虛擬鍵Ÿ即使你叫YUI的實例別的東西,記得,這是“Y”,只是字符串,而不是實際y實例。
MakeNode只會設置事件偵聽器後,從未有過(上)的聽眾,這是最常見的情況。 如果你要聽“前的事件,像往常一樣。
薩蒂揚- 2011年8月19日, #