1. 程式人生 > >angularJS系列之指令directive應用例項

angularJS系列之指令directive應用例項

程式碼結構

這裡寫圖片描述

html頁面程式碼

<!DOCTYPE html>
<html ng-app="demoForDirective">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <script src="./lib/angular.js"></script>
    <script src="./js/app.js"></script>
    <script src="./js/controller.js"
>
</script> </head> <body> <div ng-controller='demoForDirectiveController'> <!--增加指令標籤--> <testdirective testdirective-title='title'>
{{text}} </testdirective> </div> <div> <!--為原生標籤增加動態屬性--> 分數:<input type="text"
attrsdirective/>
<br/> </div> <!--通過控制器控制子元素--> <div ng-controller="SomeController"> <accordion> <expander class="expander" ng-repeat="expander in expanders" expander-title="expander.title">
{{expander.text
}}
</expander> </accordion> </div> </body> </html>

app.js程式碼

/**
 * Created by jywl on 2016/7/7.
 */
var demoForDirective = angular.module('demoForDirective', ['demoForDirective.controller']);
demoForDirective.directive('testdirective', function () {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        scope: {
            title: '=testdirectiveTitle'
        },
        template: '<div>'
        + '<div class="title" ng-click="toggle()">{{title}}</div>'
        + '<div class="body" ng-show="showMe" ng-transclude></div>'
        + '</div>',
        link: function (scope, element, attrs) {
            scope.showMe = false;
            scope.toggle = function toggle() {
                scope.showMe = !scope.showMe;
            }
        }
    }
});

demoForDirective.directive('attrsdirective', function () {
    return {
        link: function (scope, elements, attrs, controller) {
            elements[0].onkeyup = function () { //從元素列表中獲取元素,並繫結相應的事件
                //下面的意思是,如果輸入10以內的數,則輸入框邊框顏色不變,否則變為紅色
                if (isNaN(this.value) || this.value < 1 || this.value > 10) {
                    this.style.borderColor = 'red';
                }
                else {
                    this.style.borderColor = '';
                }
            };
        }
    }
});

demoForDirective.directive('accordion', function () {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        template: '<div ng-transclude></div>',
        controller: function () { //宣告一個內部的controller,用於傳遞值和方法
            var expanders = [];
            this.gotOpened = function (selectedExpander) {
                //angularJS的forEach用法
                /*var objs =[{a:1},{a:2}];
                 angular.forEach(objs, function(data,index,array){
                 //data等價於array[index]
                 console.log(data.a+'='+array[index].a);
                 });
                 引數如下:
                 objs:需要遍歷的集合
                 data:遍歷時當前的資料
                 index:遍歷時當前索引
                 array:需要遍歷的集合,每次遍歷時都會把objs原樣的傳一次。
                * */
                angular.forEach(expanders, function (expander) {
                    if (selectedExpander != expander) {
                        expander.showMe = false;
                    }
                });
            }

            this.addExpander = function (expander) {
                expanders.push(expander);
            }
        }
    }
});

demoForDirective.directive('expander', function () {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        require: '^?accordion',//意思是將accordion的控制器傳到指令中,從而在下方使用它的函式 ^的意思是需要遍歷dom樹,?的意思是找不到不報錯
        scope: {
            title: '=expanderTitle'
        },
        template: '<div>'
        + '<div class="title" ng-click="toggle()">{{title}}</div>'
        + '<div class="body" ng-show="showMe" ng-transclude></div>'
        + '</div>',
        link: function (scope, element, attrs, accordionController) {
            scope.showMe = false;
            accordionController.addExpander(scope);

            scope.toggle = function toggle() {
                scope.showMe = !scope.showMe;
                accordionController.gotOpened(scope);
            }
        }
    }
});

controller.js程式碼

/**
 * Created by jywl on 2016/7/7.
 */
angular.module('demoForDirective.controller', [])
    .controller('demoForDirectiveController', function ($scope) {
        $scope.title = '點選展開';
        $scope.text = '這裡是內部的內容。';
    })

    .controller('SomeController', function ($scope) {
        $scope.expanders = [
            {title: 'click me', text: 'one click'},
            {title: 'click me two', text: 'two click'},
            {title: 'click me three', text: 'three click'}];
    });

程式碼說明

主要詳解app.js中關於指令的程式碼,大部分程式碼都有註釋,如果還有不清楚的可以留言,我看到會及時回覆的。

directive的引數說明

priority(優先順序)- 當有多個directive定義在同一個DOM元素時,有時需要明確它們的執行順序。這屬性用於在directive的compile function呼叫之前進行排序。如果優先順序相同,則執行順序是不確定的(經初步試驗,優先順序高的先執行,同級時按照類似棧的“後繫結先執行”。另外,測試時有點不小心,在定義directive的時候,兩次定義了一個相同名稱的directive,但執行結果發現,兩個compile或者link function都會執行)。據官方說,ng-repeate的優先順序是1000。該屬性預設值為0。

terminal(最後一組)- 如果設定為”true”,則表示當前的priority將會成為最後一組執行的directive。任何directive與當前的優先順序相同的話,他們依然會執行,但順序是不確定的(雖然順序不確定,但基本上與priority的順序一致。當前優先順序執行完畢後,更低優先順序的將不會再執行)。

scope - 如果設定為:
true - 將為這個directive建立一個新的scope作用域,而不是繼承父作用域。

false - 預設值,使用現有的scope作用域。

{/屬性名和繫結風格/} - 獨立的scope。

獨立scope與一般的scope的區別在於它不是通過原型繼承於父scope的。這對於建立可複用的元件是很有幫助的,可以有效防止讀取或者修改父級scope的資料。

繫結策略

@或@attr - 建立一個local scope property到DOM屬性的繫結。因為屬性值總是String型別,所以這個值總是返回一個字串。如果沒有通過@attr指定屬性名稱,那麼本地名稱將與DOM屬性的名稱一致。例如<widget my-attr=”hello {{name}}”>,widget的scope定義為:{localName:’@myAttr’}。那麼,widget scope property的localName會映射出”hello {{name}}”轉換後的真實值。name屬性值改變後,widget scope的localName屬性也會相應地改變(僅僅單向,與下面的”=”不同)。name屬性是在父scope讀取的。

=或=expression(綁定當前屬性,它帶來一個來自指令父scope的屬性) - 在本地scope屬性與parent scope屬性之間設定雙向的繫結。如果沒有指定attr名稱,那麼本地名稱將與屬性名稱一致。例如,widget定義的scope為:{localModel:’=myAttr’},那麼widget scope property “localName”將會對映父scope的“parentModel”。如果parentModel發生任何改變,localModel也會發生改變,反之亦然。(雙向繫結)

&或&attr - 傳遞一個來自父scope的函式,稍後呼叫。

controller - controller 建構函式。controller會在pre-linking步驟之前進行初始化,並允許其他directive通過指定名稱的require進行共享(看下面的require屬性)。這將允許directive之間相互溝通,增強相互之間的行為。

controller預設注入了以下本地物件:
$scope - 與當前元素結合的scope
$element - 當前的元素
$attrs - 當前元素的屬性物件
$transclude - (A transclude linking function pre-bound to the correct transclusion scope)

require - 請求另外的controller,傳入當前directive的linking function中。require需要傳入一個directive controller的名稱。如果找不到這個名稱對應的controller,那麼將會丟擲一個error。名稱可以加入以下字首:
? - 不要丟擲異常。這使這個依賴變為一個可選項。
^ - 允許查詢父元素的controller

restrict - EACM的子集的字串,它限制directive為指定的宣告方式。如果省略的話,directive將僅僅允許通過屬性宣告:
E - 元素名稱: <my-directive></my-directive>
A - 屬性名: <div my-directive=”exp”></div>
C - class名: <div class=”my-directive:exp;”></div>
M - 註釋 : <!-- directive: my-directive exp -->

template - 如果replace 為true,則將模版內容替換當前的HTML元素,並將原來元素的屬性、class一併遷移;如果為false,則將模版元素當作當前元素的子元素處理。

templateUrl - 與template基本一致,但模版通過指定的url進行載入。因為模版載入是非同步的,所以compilation、linking都會暫停,等待載入完畢後再執行。

replace - 如果設定為true,那麼模版將會替換當前元素,而不是作為子元素新增到當前元素中。(注:為true時,模版必須有一個根節點)

transclude - 編譯元素的內容,使它能夠被directive所用。(如果當前元素,希望其他directive呼叫,就將其設為true,並在元素的節點上,增加<div ng-transclude></div> 就好,更高階的用法,目前我還在學習中)
true - 轉換這個directive的內容。(這個感覺上,是直接將內容編譯後搬入指定地方)
element - 轉換整個元素,包括其他優先順序較低的directive。(像將整體內容編譯後,當作一個整體(外面再包裹p),插入到指定地方)

compile - 編譯函式,整個執行週期中只執行一遍。(一般情況下,用compile即可。)

link - 連結函式,可以多次執行。具體用法參照上文程式碼