1. 程式人生 > >AngularJS中Directive(指令)機制詳解

AngularJS中Directive(指令)機制詳解

AngularJS 通過被稱為 指令 的新屬性來擴充套件 HTML。
AngularJS 通過內建的指令來為應用新增功能。
AngularJS 允許你自定義指令。
指令的作用:實現語義化標籤。
使用AngularJS的directive(指令)機制,我們可以實現這樣的東西:

<body ng-app="myApp">

<directive></directive>
<script src="js/angular.js"></script>
<script>
var app = angular.module("myApp"
, []); app.directive("directive", function() { return { restrict:'E', template : "<h1>自定義指令!</h1>", replace:true }; });
</script> </body>

結果是:
這裡寫圖片描述
html標籤結構如下:
這裡寫圖片描述
可以看到瀏覽器可以解析自定義的directive指定,將自定義指令替換成template中的內容。這就是replace:true的作用,至於restrict:’E’這個配置項的含義,請看下錶:
這裡寫圖片描述


你可以限制你的指令只能通過特定的方式來呼叫。通過新增 restrict 屬性,並設定只值為 “E”, 來設定指令只能通過元素名的方式來呼叫。
restrict 預設值為 EA, 即可以通過元素名和屬性名來呼叫指令。
transclude(變換):

<body ng-app="myApp">
<directive><br>
    <span>這是內嵌元素</span>
</directive>
<script src="js/angular.js"></script>
<script>
var app = angular.module("myApp", []); app.directive("directive", function() { return { restrict : "E", template : "<h1>自定義指令!<span ng-transclude></span></h1>", transclude:true }; }); </script> </body>

結果是:
這裡寫圖片描述
html標籤結構變成了這樣:
這裡寫圖片描述
程式碼裡面多了個transclude:true,於是內部出現了子標籤。
transclude就是用來處理自定義標籤內出現子標籤的情況。
關於compile和link
指令的本質其實是一個替換過程。這個過程分2個階段,也就是compile(編譯)和link(連線)。
簡而言之,compile階段進行標籤解析和變換,link階段進行資料繫結等操作。這裡面更加細節的處理過程請參見《AngularJS》這本書中的解析。
舉個栗子

var expanderModule=angular.module('expanderModule', [])
expanderModule.directive('expander', function() {
    return {
        restrict : 'EA',
        replace : true,
        transclude : true,
        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) {
            scope.showMe = false;
            scope.toggle = function toggle() {
                scope.showMe = !scope.showMe;
            }
        }
    }
});
expanderModule.controller('SomeController',function($scope) {
    $scope.title = '點選展開';
    $scope.text = '這裡是內部的內容。';
});

HTML程式碼:

<html ng-app='expanderModule'>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <script src="../angular-1.0.3/angular.min.js"></script>
        <link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
    </head>
    <body>
        <div ng-controller='SomeController'>
            <expander class='expander' expander-title='title'>
                {{text}}
            </expander>
        </div>
    </body>
    <script src="ExpanderSimple.js"></script>
    <script src="js/angular.js"></script>
</html>

CSS程式碼:

.expander {
    border: 1px solid black;
    width: 250px;
}

.expander>.title {
    background-color: orange;
    color: white;
    padding: .1em .3em;
    cursor: pointer;
}

.expander>.body {
    padding: .1em .3em;
}

效果如下:
這裡寫圖片描述
專案開發中經常會用到link進行事件的晚繫結。
綜合複雜的栗子

JS程式碼:

var expModule=angular.module('expanderModule',[])
expModule.directive('accordion', function() {
    return {
        restrict : 'EA',
        replace : true,
        transclude : true,
        template : '<div ng-transclude></div>',
        controller : function() {
            var expanders = [];
            this.gotOpened = function(selectedExpander) {
                angular.forEach(expanders, function(expander) {
                    if (selectedExpander != expander) {
                        expander.showMe = false;
                    }
                });
            }
            this.addExpander = function(expander) {
                expanders.push(expander);
            }
        }
    }
});

expModule.directive('expander', function() {
    return {
        restrict : 'EA',
        replace : true,
        transclude : true,
        require : '^?accordion',
        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);
            }
        }
    }
});

expModule.controller("SomeController",function($scope) {
    $scope.expanders = [{
        title : 'Click me to expand',
        text : 'Hi there folks, I am the content that was hidden but is now shown.'
    }, {
        title : 'Click this',
        text : 'I am even better text than you have seen previously'
    }, {
        title : 'Test',
        text : 'test'
    }];
});

HTML程式碼:

<html ng-app="expanderModule">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <script src="../angular-1.0.3/angular.min.js"></script>
        <link rel="stylesheet" type="text/css" href="Accordion.css"/>
    </head>
    <body ng-controller='SomeController' >
        <accordion>
            <expander class='expander' ng-repeat='expander in expanders' expander-title='expander.title'>
                {{expander.text}}
            </expander>
        </accordion>
    </body>
    <script src="Accordion.js"></script>
</html>

CSS程式碼:

.expander {
    border: 1px solid black;
    width: 250px;
}

.expander>.title {
    background-color: black;
    color: white;
    padding: .1em .3em;
    cursor: pointer;
}

.expander>.body {
    padding: .1em .3em;
}

重難點是子Expander裡面訪問外層Accordion的scope中的資料。