AngularJs(Part 11)--自定義Directive


先對自定義Directive有一個大體的映像

myModule.directive('myDirective',function(injectables){
    var directiveDefinitionObject={
        restrict:string,
        priority:number,
        template:string,
        templateUrl:string,
        replace:bool,
        transclude:bool,
        scope:bool or object,
        controller:function controllerConstructor($scope,$element,$attrs,$transclude){...},
        require:string,
        link:function postLink(scope,iElement,iAttrs){...},
        compile:function compile(tElement,tAttrs,transclude){
            ...
            return {
                pre:functionLink(scope,iElement,iAttrs,controller){...},
                post:functionLink(scope,iElement,iAttrs,controller){...}
            };
        }
    };
});
View Code

 重點在directiveDefinitionObject這個對象。這個對象當中有些屬性是互相排斥的,即:有了A屬性,就不需要B屬性。但大部分是可選的

以下是詳細解釋各個屬性的作用
1. restrict: 決定directive是做HTML tag,屬性,類或者是注釋,只能在 'E','A','C','M'或者這四個字母的任意組合中取值。參考下表
    值      例子
    E        <my-directive></my-directive>
    A        <div my-directive='abc'></div>
    C        <div class='my-directive:abc'></div>
    M        <!--this is abc my-directive:abc -->
    restrict默認是A,如果一個directive既想用在Tag中也想用在注釋中,那么就用 'EM'。

   注意:Directive名字是myDirective,但是使用時,無論是在HTML tag,還是屬性,還是類中都是 my-directive。 如果Directive名字是LoginInBar,那么使用時就用login-in-bar.(AngularJS會進行奇怪的名稱轉換
2. priority 設置優先級,數字大的先運行。一個例子是ng-repeat ,必須要先跑ng-repeat,生產出重復的對象,再跑其他
3. template和templateUrl 模板或者模板地址。有時我們把一大段HTML片段包裝在一個AngularJS中,如我只想用一個<hello></hello>來表示<div>hello <span style='color:red;'>world</span></div>那么可以把template設置為這一段字符串,或者寫道另一個文件中用templateUrl引用。當然,還可以通過其他方法加載模板,然后把它們存在$templateCache中,如下。
   module.run(function($templateCache){
        $templateCache.put("a.html","<div>hello <span style='color:red;'>world</span></div>");
   });
4. transclude 有時我們想保留原始的內容,並把它們添加到模板中,如有<hello>,we have fun</hello>。 想實現的效果是<div>hello world, we have fun</div>。如果我們設置了template為

<div>hello world</div>,那么"we have fun"這一段不見了,現在設置transclude為true,並把template設置為<div>hello world<span ng-transclude></span></div>,就可以達成需要的效果了。注意template中多了個ng-transclude
5.compile和link
    AngularJS的directive要起作用必須經過兩個階段:
    1) compile階段,在該階段AngularJS搜尋template中的整個DOM樹,找到所有注冊的其他directive,把他們按照各自的templat、replace等設置轉換為DOM,運行各自的compile的function
    2) link階段,在該階段主要負責保持model和view的一致性,就是各種加監聽器。在這個階段scope在被注入到link中,所有的directive才有用
    
    ...
    
6.scope AngularJS提供了三種scope給directive
    1).就用DOM中已經存在的scope  (false)
    2).繼承自從已經存在的scope,可以訪問父級scope中的所有properties  (true)
    3).一個獨立的scope。不可以訪問父級scope的properties ,但他有一個$parent屬性指向父級scope  (一個{/*properties*/}對象)
    對於前兩種沒什么好說的,對於第三種,看一個例子。
    現在想完成一個點擊之后顯示一些div的功能。HTML代碼如下:
    <div ng-controller='MyController'>
        <expander expand-title='title' expand-text='text' expand-click='change()'></expander>
    </div>
    javascript代碼如下:

angular.module('test',[])
        .controller('MyController',['$scope',function($scope){
                $scope.title='click me i am the title';
                $scope.text='well ,i was hidden but now i am shown';
                $scope.change=function(){
                    $scope.text='haha i change myself';
                };                
            }])
        .directive('expander',function(){
            return {
                restrict:'E',
                replace:true,
                transclude:true,
                template:['<div>',
                            '<div id="d1" ng-click="toggle()">{{scopeTitle}}</div>',
                            '<div id="d2" ng-show="showMe" ng-transclude>{{scopeText}}</div>',
                            '<div id='d3' ng-click="scopeChange()">click me to change</div>',
                          '</div>'].join(''),
                scope:{scopeTitle:'@expandTitle',
                        scopeText:'=expandText',
                        scopeChange:'&expandClick'},
                link:function(scope,element,attributes){
                    scope.showMe=false;
                    scope.toggle=function(){
                        scope.showMe=!scope.showMe;
                    };
                }
            };
        })
        ;
View Code

    結果是在d1中顯示的是title,因為scopeTitle取得是'@expandTitle',意思是把expand-title的屬性值當作字符串傳給scopeTitle;

    而d2中顯示的是“well ,i was hidden but now i am shown”,因為scopeText取得是'=expandText',意思是把expand-text的值當作變量名,在父級scope中找到同名的變量,把這個變量的值傳給           scopeText;
    再看d3中的ng-click,它的值是通過scopeChange:'&expandClick'傳進來的,expand-click的值是父級scope中的scope方法。所以’&'可以用來傳遞方法。

4.controller directives之間需要互相通信是,可以通過controller來進行
    controller: function($scope,$element,$attrs,$transclude){}
    其他的directive可以接受這個controller:通過require屬性 require:"^?directiveName".
    directiveName:是駝峰表示法的directive的名字。需要哪個directive的controller就寫哪個的名字;
    ^:默認的AngularJS是找同一個元素內的directive,如果加上了“^”,AngularJS沿着DOM樹往上找,知道找到為止;
    ?:如果沒找到controller,AngularJS會丟出一個錯誤,加上了"?"表示沒找到也沒關系;
    重寫一個例子,要求出現三個tag,點擊其中一個的話,自己展開,別的收縮  

angular.module('test',[])
        .directive('accordion',function(){
            return {
                restrict:"E",
                replace:true,
                transclude:true,
                template:'<div ng-transclude></div>',
                controller:function(){
                    var expanders=[];
                    this.getOpened=function(selected){
                        angular.forEach(expanders,function(expander){
                            if(selected!=expander){
                                expander.showMe=false;
                            }
                        });
                    };
                    this.addExpander=function(expander){
                        expanders.push(expander);
                    };
                }
            };
        })
        .directive('expander',function(){
            return {
                restrict:'E',
                replace:true,
                transclude:true,
                require:"^?accordion",
                template:['<div>',
                            '<div ng-click="toggle()">{{scopeTitle}}</div>',
                            '<div ng-show="showMe" ng-transclude>{{scopeText}}</div>',
                          '</div>'].join(''),
                scope:{scopeTitle:'=expandTitle',
                        scopeText:'=expandText',
                      },
                link:function(scope,element,attrs,accordionController){
                    scope.showMe=false;
                    accordionController.addExpander(scope);
                    scope.toggle=function(){
                        scope.showMe=!scope.showMe;
                        accordionController.getOpened(scope);
                    };
                }
            };
        })
        .controller('MyController',['$scope',function($scope){
            $scope.expanders=[{
                title:'click1',
                text:'text1'
            },{
                title:'click2',
                text:'text2'
            },{
                title:'click3',
                text:'text3'
            }];
        }]);
View Code

    再看HTML:
    <div ng-controller='MyController'>
        <accordion>
            <expander ng-repeat='expander in expanders' expand-title='expander.title' expand-text='expander.text'></expander>
        <accordion>
    </div>
    可以想見,我要實現一個展開就其他收縮的功能,我得有一個地方存儲所有expander的狀態。盡管在expander的link中的scope可以訪問到MyController的scope從而可以找到expanders,但是最好不要這樣做,還是隔離的好。那么最好的地方就是在expander的父元素accordion中存儲。accordion相當於一個倉庫,他提供了API供別人使用。
   


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com