有關自定義指令的scope參數,網上很多文章都在講這3種綁定方式實作的效果是什麼,但幾乎沒有人講到底怎麼使用,本篇希望聊聊到底怎麼用這個話題。
一. 自定義指令
自定義指令,是
Angularjs
用來實作元件化的方式,相比于
React
和
Vue
的元件化方式,它真的很複雜,自定義指令太重了,它暴露了太多可供定制的參數,以至于普通的開發者完全不知道要用它來做什麼而将其束之高閣,畢竟一般的業務邏輯通過controller和service就已經可以完成了。
自定義指令在
Angularjs
項目中主要有兩大用途:
- 1.封裝指定元件的DOM操作
期望的開發方式是将DOM的操作盡可能封裝在自定義指令中,這樣對于局部變量的操作會更容易加入到Angular自己的生命周期中。Angularjs
- 2.元件化
靠自定義指令實作元件化。諸如你在Angularjs
React
中看到的類似于,這樣的自定義标簽,或是父級子級傳值所使用的prop,又或者是标記元件自身狀态的state,在Vue
中全部都是通過自定義指令來實作的。Angularjs
二. 資料綁定的形式
自定義指令在定義後,需要在html檔案中編寫,最常用的方式是将其書寫為标簽屬性。當使用自定義指令時,常常需要将一個變量的值從controller傳遞至directive中,此時需要在
scope
屬性中進行變量綁定設定,
Angularjs
提供了3種不同的綁定方式(實際上也可以直接傳遞True),如下所示:
scope: {
infiniteScroll: '=', // 将infiniteScroll同父級controller中的指定對象雙向綁定
onSend: '&', // 從父級擷取一個變量的引用,常用作方法調用
fromName: '@' // 從父級擷取值後便隻在本地作用域生效
}
關于三種綁定方式使用的方法,網上可以搜到非常多的文章,本篇不再贅述,今天我們隻來詳細看一下這幾種方式的使用場景和差別。
2.1 @綁定
@綁定
可以轉移常量指派的位置,常用于為自定義封裝元件暴露一個可設定常量參數的接口。這種綁定方式的意義,在于從自定義指令外部(一般是從html頁面上綁定一個常量或控制器中的變量)擷取一個局部變量的值。
實際場景:
例如我們封裝了一個分頁元件,其中指令局部作用域中的
displayPaginationNums
屬性用于決定分頁元件的頁碼欄顯示多少個按鈕,然後把剩餘的按鈕收起來并添加
...
按鈕,這是一個很常見的需求。
-
不使用@綁定
不使用@綁定,完全可以做到,隻需要在link函數裡,初始化為其指派即可。
link:function(scope, elements, attrs){
scope.displayPaginationNums = 5;//用于決定分頁導航欄最多可顯示幾個數字
},
使用這樣的方式,就可以,但我們預設了一個前提,那就是所有調用這個元件的人,都會浏覽這個元件的源代碼。這其實是很不友善的,換位思考一下,你使用
Angularjs
的時候,會先去源碼裡找一下對應的方法開頭都定義了哪些變量,哪些可以修改嗎?當然不會。
這個屬性在不同的項目中都會需要指派,但需要動态去修改的場景其實并不多,是以我們需要将接口暴露至更高的開發層級,供調用者直接指派。
-
使用@綁定
當使用@綁定後,我們實際上是面向調用者暴露了去設定重要參數的接口,使用起來更加友善。下面的寫法讓開發者使用這個元件時,可以在代碼編寫時友善地傳入自己想要設定的值:
//指令定義時
scope:{
displayPaginationNums:'@'
},
<!--指令調用時-->
<div table-pagination
display-pagination-nums="5">
面向對象程式設計原則中有一個重要的原則,叫做開放封閉原則,它的意思是說,你在程式設計中所書寫的代碼,應該對擴充開放,對修改封閉。簡單地說就是你所編寫的代碼成型以後,在後續的使用和功能擴充的時候,盡可能不需要再去改動代碼,而隻需要通過編寫與擴充相關的代碼即可。
此處就是從封閉轉為開放的一個示例,雖然看起來很細小,但可以很明确地表達這個原則。
2.2 &綁定
&綁定
用于傳遞父級函數的引用,用來調用父級控制器中定義的方法。如果隻是以業務邏輯為子產品進行封裝,這種綁定方式可以幫我們避免一部分代碼重複,如果是為通用架構編寫純元件,則可以為調用者提供自定義函數的接口。
比如我們在制作一個表格和分頁元件時,表格每一頁隻顯示10條資料,分頁是背景來完成的,那麼每一次點選分頁元件上的頁碼按鈕時,我們都需要向背景發送ajax請求來擷取新一頁的資料。那麼這個發送ajax請求的方法你會寫在哪裡呢?
不使用&綁定
-
将方法寫在controller中
優勢:這樣做的好處是如果以後我們需要增加一個輸入框來實作精确跳轉到哪一頁時,可以直接在模闆中使用
ng-change="sendAjax( )"
來綁定這個方法,友善複用,擴充,甚至修改功能。
劣勢:但這樣做的話,如果想在自定義指令中就無法直接調用這個方法,常見的處理政策是在自定義指令中使用
将一個自定義事件發送至父級controller,在父級controller中使用scope.$emit( )
來監聽這個自定義事件,并在回調中執行$scope.$on( )
這個方法。$scope.sendAjax( )
-
将方法寫在指令的link函數中
優勢:可以将一些不需要使用者感覺的函數封裝起來,例如資料發送前的校驗,或是響應資料的結構重組等,提高業務邏輯相關的代碼在controller中的比重,減小controller的體積。
劣勢:當其他元件想要使用這個方法時會很困難,Angularjs并沒有提供一種跨directive調用方法的機制。
實際上在開發過程中,不熟悉
的開發者在使用自定義指令時,幾乎都會選擇将方法寫在controller中并通過消息機制來觸發這個函數(也就是上文中第一個方法),他們希望指令所封裝的元件是純粹的,換句話說,它是可複用且與業務邏輯剝離的。&綁定
使用&綁定
-
對于業務邏輯開發而言
簡潔且容易使用,元件可直接調用controller中的業務邏輯代碼,避免了當自定義事件過多時造成的controller中充滿了事件監聽的回調方法的問題,使用方法如下:
//主模闆中
<div change-page="sendAjax"></div>
//指令定義中
...
template:'<div ng-click="changePage()"></div>'
scope:{
changePage: '&'
},
...
-
對于子產品封裝而言
從上面的示例就可以看出,自定義指令中實際執行的
方法,是使用者在使用這個元件時編寫在controller之中的sendAjax( )這個方法,當我們需要封裝一個供其他開發者調用的元件時(往往是在編寫一個元件庫),這種結構是在angular中最自然的實作方式。changePage( )
當你希望給一個自定義指令暴露越來越多個性化定制接口時,它很可能變得臃腫,甚至一無是處。
&綁定
意義,在于将業務邏輯從元件中剝離出來,但過多的可定制性又會給開發者帶來額外的問題,你會發現,僅僅是簡單地使用一個下拉框或是勾選框之類的簡單元件時,就需要傳入一大堆自定屬性,而這本該是在互動設計标準中确定好并編寫在項目中的指定位置的。自定義指令的可定制性越高,html模闆的體積就會越大,controller中的代碼量也會随之增大,帶來的直接問題就是:開發很友善,維護很痛苦。
2.3 =綁定
=綁定
是3中綁定形式中最常用的一種,常用于将用于渲染的數組或對象傳入自定義指令中。這樣做可以将業務邏輯分塊,使得代碼結構更具有層次性,降低維護難度。
一個表格元件,需要通過ajax請求從背景擷取100條用于展示的資料,這些資料可能需要排序,過濾,分頁等操作,首先應該明确的是,即時這些代碼全部寫在controller中,程式也是可以運作的,隻是當你在其他場合需要複用時,就需要複制粘貼很多代碼。那麼該如何來設計這樣一個功能并提取公用元件呢?
排序
,
過濾
分頁
都是表格元件的通用動作,也就是說與資料對象本身的結構并沒有太大關系,對于一個通用型表格控件來說,我們唯一必須要傳入的隻有一項——資料源,且它是有可能會随着使用者操作而發生變化的。
推薦的技術方案為:
- service : 封裝$http操作,資訊提示,及容錯處理
- controller : 調用service暴露的方法從背景擷取資料,并指派給指定變量
- directive : 雙向資料綁定controller中的變量以擷取驅動表格渲染的資料,将排序,過濾,分頁的具體實作封裝在指令内部。
這樣的結構,使宏觀業務邏輯,前背景資訊互動,元件通用功能分别在不同的子產品中實作,可以極大提高定位問題的速度。
=綁定
的雙向資料綁定在使用中是存在一些方法問題的,詳情請參考
《Angularjs1.X進階筆記(1)—兩種不同的雙向資料綁定》
。
三. 自定義指令的實用意義
-
—— 常用于傳遞從背景擷取的用于驅動純元件的源資料。=綁定
-
—— 為自定義指令中傳遞可配置的常量參數提供設定接口。@綁定
-
—— 為自定義指令中傳遞自定義方法提供接口。&綁定