一、directive中的scope
directive無疑是AngularJS中比較復雜難懂的部分,而directive中個scope更是其中最復雜的部分了,尤其是在嵌套directive中互相通訊的時候。
單獨的directive中,scope概念還是比較簡單的,此時scope的三種取值情況為:
- scope:false 此時,directive沒有獨立的scope對象,link函數中引用的scope對象來自於當前節點的默認controller
- scope:true 此時,directive擁有獨立的scope對象,此scope是由父scope對象繼承而來,可以訪問父scope中的所有屬性,此時實際上是通過繼承鏈接(prototype鏈)訪問的父scope中的屬性,要注意的是,當給此scope繼承而來的屬性名稱賦值的時候,子scope中會相應建立一個本地屬性,此時改變的是本scope的變量屬性,父scope中的屬性是不會改變的。
- scope:{propertyName:"=@propertyValue"} 此時,directive中擁有一隔離的scope對象,其實就是一個全新的scope對象,和上面取值為true時的區別就是不能訪問父scope中的屬性,當然還是可以通過$parent屬性去訪問父scope對象中屬性的。
當情況改變到嵌套directive中的子(或者說內部)directive中時,內部的directive的scope和我們的理解或習慣性的的認知有所不同,而造成這一切的原因是,AngularJS中其實根本沒有嵌套directive一說!只要我們腦子里還把內部的directive當成獨立的directive來看待就會明白了。比如我們要實現如下的嵌套directive:
<div my-grid height="500" width="300"> <div my-grid-row height="300"> <div my-grid-cell width=".33"> </div> <div my-grid-cell width=".34"> </div> <div my-grid-cell width=".33"> </div> </div> <div my-grid-row height="200"> <div my-grid-cell width=".5"> </div> <div my-grid-cell width=".5"> </div> </div> </div>
var $myd=angular.module("myDirectives",[]); $myd.directive("myGrid",function(){ return{ restrict:"A", scope:{height:"@height",width:"@width"}, link:function(scope,el,attrs){ } }; }); $myd.directive("myGridRow",function(){ return{ restrict:"A", scope:true, link:function(scope,el,attrs){ } }; }); $myd.directive("myGridCell",function(){ return{ restrict:"A", scope:true, link:function(scope,el,attrs){ } }; });
開上面代碼可知myGrid設置了隔離的scope,myGridRow和myGridCell申請了繼承的獨立scope,現在猜猜,myGridRow和myGridCell的scope.$parent是誰,是不是myGrid的scope?不知道又沒有tx認為(注意下面是示意代碼,不是真的如此引用myGridCell和myGridRow的scope)
myGridCell.scope.$parent=myGridRow.scope
myGridRow.scope.$parent =myGrid.scope
如果你也和我以前一樣這樣認為,那么就大錯特錯了,實際上應該是這樣的:
myGridCell.scope.$parent=myGridRow.scope.$parent=myGrid.scope.$parent
也就是說,這三級的directive的scope的父scope對象是一個人,也就是myGrid所在div元素的父元素或祖先元素節點上指定的ngController或者通過路由設置給出的controller。
如果myGridRow和myGridCell的scope設為false或是不設置呢,此時myGrid有獨立scope而myGridRow和myGridCell都沒有,因此分享myGrid父元素的controller的scope,也就是:
myGrid.scope.$parent=myGridRow.scope=myGridCell.scope
而如果myGridCell和myGridRow的scope也設置為隔離的scope({}),那么此時和第一種情況一樣,三個directive的父scope對象也都是同一個對象:myGrid父元素的controller的scope。
二、嵌套directive的通訊
以上研究了嵌套directive下的scope,可以看到我們不可能通過訪問父或子directive中scope方式進行通訊,那么我們究竟應該怎樣在各級directive中通訊呢?我知道的有兩種方式:
1,通過注入$rootScope,然后在$rootScope中調用$broadcast和$on,發送和響應事件,這顯然不是好的設計。
2,通過注入父directive的controller。我們看文檔知道,directive有一個require屬性,這里明確說明require的是指定的directive的controller,此時link函數可以有第四個參數,就是這個controller。下面看示意代碼。
var $myd=angular.module("myDirectives",[]); $myd.directive("myGrid",function(){ return{ restrict:"A", scope:{height:"@height",width:"@width"},
controller:function($scope){
$scope.getHeight=function(){
return $scope.height;
};
$scope.getWidth=function(){
return $scope.width;
};
}, link:function(scope,el,attrs){
scope.height=parseInt(scope.height);
$scope.width=parseInt(scope.width); } }; }); $myd.directive("myGridRow",function(){ return{ restrict:"A", scope:true,
require:"^myGrid",
controller:function($scope){
$scope.getWidth=function(){
return $scope.pCtrl.getWidth();
}
}, link:function(scope,el,attrs,pCtrl){
scope.pCtrl = pCtrl;
scope.pHeight = pCtrl.getHeight();
scope.height=parseInt(attrs["height"]);
} }; }); $myd.directive("myGridCell",function(){ return{ restrict:"A", scope:true,
require:"^myGridRow", link:function(scope,el,attrs,rowCtrl){
scope.widthRatio = parseFloat(attr["width"]);
scope.width=scope.widthRatio * rowCtrol. } }; });
由上面代碼可見,父子directive通訊需要的兩個要素:
- 父directive中聲明controller函數,這和我們平時聲明controller差不多,同樣可以注入$scope,不同的是這里還可以注入$element,$attrs等,詳細的請相關查閱資料。
- 子directive中添加require屬性,內容就是父directive的名字,前面添加"^"是告訴AngularJS到上級的元素中查找父directive的controller,而不是在同元素中找。如果子directive可以脫離父directive單獨用,還需要加上"?",以來通知AngularJS,不是必須的,此時link的第四參數有可能為空。
注意:上述代碼只是示意代碼,並不能真正工作,如果你使用上述代碼將會發現link函數中並不能獲取到真正的數值。這是因為例子中嵌套directive是從內到外的順序來初始化的,子directive的link函數調用時,AngularJS只是初始化了父Directive對象的Controller對象,父directive的link函數並沒有調用過,所以你只能取到空值。我的解決方法時子directive在父directive中注冊回調函數,然后由父directive在適當的時候回調子directive的方法,或者注冊后就由父directive來布局子directive的html元素,具體要看你的目標是什么來定了。