2016年5月2日 星期一

漆。發揮創造力,自己做出想像中的客制化套件(directive)!

圖片來源:http://ppt.cc/JVPxN
有時候開發程式,我們常常會先上網找別人寫過類似的程式或是第三方套件(3-party)
但往往找到的程式沒辦法100%完全符合我們的需求(廢話
AngularJs提供了一個特別的功能,叫做directive
directive是用來做什麼的呢?
可以自定html的內容與行為,做出來就像是一個客制化的套件
它能夠「簡化重複的code,也能減少DOM操作」。

舉例:
像是台灣的地址包含「郵遞區號」與「地址」兩個
通常我們遇到這種我們都要做兩個input來處理這件事
但同時我們又希望這兩個input的值是同一個輸出(output)
這時我們可以利用directive來處理!
Live Demo如下

directive要怎麼開始?

使用上,他的使用方法跟上一篇講到的ngMessage有點像
不過因為我們是客制化的方法(官方提供directive使用方法不同,請見上一篇ngMessage)
所以我們是直接掛在ng-app底下,如下圖

directive分為四種限制(restrict)

  • A:作為Html屬性(attribute)【常用】
    • 用法:自訂directive為abc是屬性,restrict: 'A'
  • E:作為Html元素(element)【常用】
    • 用法:自訂directive為abc是元素restrict: 'E'
  • C:作為Html類別(class)【常用】
    • 用法:自訂directive為abc是類別restrict: 'C'
  • M:作為Html註解(comment)【非常之少少少少用】
    • 用法:自訂directive為abc是註解,restrict: 'M'
《這詭異的comment,說真的實用上真的用不到,更神奇的是我照著官方教學寫還沒有反應,更不用說在Google找到寫關於comment的方法或探討,這大概是ng裡面我覺得最詭異的一個東西了.....WTF》

以上四種限制可以複合使用,像是 restrice: 'AEC
如此一來,這個directive就可以接受A、E、C這三種限制(都可以使用)

這邊我做了一個簡單的Demo,讓大家可以輕鬆理解四種限制

為什麼directive名字不一樣!?

在Demo中,大家應該會發現
我在directive的命名是「myDirective」,但使用卻變成「my-directive」
其實這個現象在ngMessage的時候也有發生過唷!
這其實是ng對於命名上的一個正規化(Normalization)
我們以「myDirective」為例子,以下五種都是可以對應的名稱!
  • my-directive
  • my_directive
  • my:directive
  • data-my-directive
  • x-my-directive
雖說可以搭配Html驗證工具加上前綴字(x-或是data-),但官方不建議這樣做ng本身就提供驗證的方式,方法請見上一篇!

*貼心小連結>陸。表單驗證

template是什麼?是directive的一部份嗎?

template是directive的設定屬性(options)之一,就像是一台電腦,使用前需要把設定都設定(settings)完成,就可以照著你的想法或習慣去使用了!
那麼,directive的設定屬性(options)有哪些呢?

restrict【常用】

>型別:字串(string),預設為A,包含AECM四種可混搭。
這個屬性剛剛有解釋過,用以宣告directive的用法。

priority

>型別:數字(int)
如果同一個元素底下掛有不只一個的directive,那麼就會依照這個屬性來判斷優先順序。

template【常用】

>型別:字串(string)
使用「字串」來編輯Html內容,套用此directive的元素會被代換為這邊的Html。
ex:template: '<div>這是Directive!</div>'
*上方的demo就是利用template來呈現的!

templateUrl

>型別:字串(string)
用法類似template,但不同的是,templateUrl是用「字串」編輯檔案名稱。
ex:templateUrl: 'index.html'

replace【常用】

>型別:布林值(bool),預設為false。
如果內容設定為True,會把掛directive的元素取代掉;反之,會插入元素當中。

transclude

>型別:布林值(bool),預設為false,需搭配templdate一起使用。
可把要把掛有directive的元素「內容」插入directive的template中任意的位置!
ex:若transclude為true, templdate: '<div>這是Directive!</div><div ng-transclude></div>',其中ng-transclude就是ng提供設定「原本的元素內容」的方法。
*這也解釋了上方directive為什麼裡面的內容沒有顯示的原因!


scope【超級好用!】

>型別:物件(object)或是布林值(bool),預設為false。
四種情況會造成不同的結果,如下:
  • true:則在directive中建立一個獨立的scope,並且「繼承」上層的scope,而此scope可以在directive中使用。
  • false:則「共用」上層的scope,不會產生獨立scope。
  • {}:產生一個獨立的scope,不受上層scope影響。
  • {= or @}:指定要與上層scope關連的屬性(需要指定掛在directive元素上的屬性),用「=」表示雙向綁定,會互相影響;用「@」表示單向綁定,用法如:{ name : '=' }
這邊我也做了一個Demo,讓大家可以釐清觀念!

controller

>型別:函數(function),預設沒有。
定義directive的controller,用於directive之間分享資訊,否則請使用link function。
這個概念有點抽象,我利用一開始的「台灣地址」Demo來改寫使用controller
讓大家可以對於controller更有概念!


require【常用】

>型別:字串陣列(string array)
檢查必要的屬性,否則會出錯,
ex:require: ['ngModel'] 表示此directive所掛的元素需掛有ng-model!
另外可以在前面加上前置符號(prefix),各有意義不同:
  • ?:就算找不到也不要出錯,使其檢查通過。ex:require: ['?ngModel']
  • ^:檢查掛directive的元素與父元素的屬性存在。ex:require: ['^ngModel']
  • ^^:只檢查父元素屬性存在。ex:require: ['^^ngModel']

link【常用】

>型別:函數(function)
會被保留並在程式中重複利用,可以在此註冊監聽事件、初始化內容、與DOM元素的設定。
  • 使用時機:針對特定元素註冊事件,而且可能會用到scope
  • 用法:link: function(scope, elem, attrs){ //doing things here. }
其中function有三個參數可以用scope是這個directive的scope(若false就是上層的scope);elem代表這個掛著directive的元素;attr代表此元素的屬性。

這邊我也做一個Demo,讓大家稍微了解一下link function!


Demo程式中用到了「$scope.$apply()」的方法
用意是「執行」$apply()中的方法!
通常比較少用到這個方法,因為大部分的事件(包含ngClick、controller init或是$http的事件)都已經被ng包上$apply(),而且在$apply()中是不能重複呼叫$apply()的(會出error),所以通常使用到的情況都是瀏覽器DOM事件、setTimeout、XHR(XML Http Request) 或是第三方套件(3-Party Libraries)。

compile

>型別:函數(function)
通常你只會用到link function就能解決問題,而compile function並不像link是可以重複利用的,只會再程式一開始執行
  • 使用時機:在DOM在畫面上出現(渲染render)前,將其改變,且沒有scope
  • 特點:return為函數(function)此特點讓同樣的directive可以共用方法。
  • 用法:compile: function(tElement, tAttrs) { //doing things here. }
其中tElement跟tAttrs參數跟link function的參數意義相同,指的都是元素與元素的屬性。

這邊我也做了一個Demo,讓大家了解一下compile function!


Demo程式中用到了element.append()的方法
這是JavaScript的用法,把元素(字串型態)插入該元素底下
Demo程式中也用到了上方說過的transclude的概念,來重複利用directive元素下的子元素!

後記

AngualrJs的directive講到這裡,其實講的不多,因為directive包山包海,能做的事情太多,所以要講的東西也很多,所以本篇用了很多demo程式來解釋文章,讓大家可以在看文章的時候離現實情況更貼近一點,當然directive能做的事情還很多,就有待大家去尋找大祕寶了(欸

重點複習

directive有AECM四種restrict,可以很自由的自訂directive宣告的方法!
- 用template置換html
- 用replace選擇是否要取代元素
- 用require限制必須要有的屬性
- 用scope可以靈活的運用
- 用link去綁定要綁定的事件
directive可以做的事情很多,需要用的東西也很多,在做directive前,務必請先想想做了這個directive是否可以重複利用,否則做了一個directive只用了一次,或是只有特定的情境可以使用,其實也很可惜對吧?

下集預告

下一篇,也會是這個ng初階教學的最後一篇,技術部分討論到這邊其實已經差不多了,下一篇會以開發者的經驗來談談ng的經驗談,敬請期待囉!

那麼,我們下次見囉!

參考資料