AngularJS學習筆記(2) --- 指令參數和scope綁定策略

引言

指令(Directive)能夠說是 AngularJS 的核心,而其開發也是比較困難的,本文主要介紹指令的一些參數和scope的綁定策略。html

參數

從 AngularJS 的官方文檔中看到指令的參數以下:ajax

{
    priority: 0,
    template: '<div></div>', // or // function(tElement, tAttrs) { ... },
    // or
    // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
    transclude: false,
    restrict: 'A',
    scope: false,
    controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
    controllerAs: 'stringAlias',
    require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
    compile: function compile(tElement, tAttrs, transclude) {
      return {
        pre: function preLink(scope, iElement, iAttrs, controller) { ... },
        post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
      // or
      // return function postLink( ... ) { ... }
    },
    // or
    // link: {
    //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
    //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
    // }
    // or
    // link: function postLink( ... ) { ... }
}

下面詳細講解每一個參數。緩存

priority(Number)

指令執行的優先級,用於多個指令同時做用於同一個元素時。例如:cookie

<select>
    <option ng-repeat="i in [1, 2]" ng-bind="i"></option>
</select>

上面的例子中,ng-repeat 指令和 ng-bind 指令同時做用於 option 元素,因爲 ng-repeat 的 priority 爲1000,ng-bind 的 priority 爲0,所以先執行 ng-repeat,而後變量 i 的值才能用於 ng-bind 中。app

template(String or Function)

HTML模板內容,用於下列狀況之一:函數

  • 替換元素的內容(默認狀況)。post

  • 替換元素自己(若是 replace 選項爲 true)。ui

  • 將元素的內容包裹起來(若是 transclude 選項爲 true,後面會細說)。url

值能夠是:spa

  • 一個 HTML 字符串。例如:<div>my name is {{name}}</div>

  • 一個函數,接收兩個參數 tElement(元素自己) 和 tAttrs(元素的屬性集合),返回 HTML 字符串。

templateUrl(String or Function)

templateUrl 和 template 做用相同,但模板內容是從 $templateCache 服務或遠程 url 加載。

值能夠是:

  • 一個字符串,AngularJS 會先從 $templateCache 中查找是否緩存了對應值,若是沒有則嘗試 ajax 加載。例如:在頁面中有以下 script:

    <script type="text/ng-template" id="Hello.html">
        <p>Hello</p>
    </script>

    AngularJS 會將 type="text/ng-template"script 標籤中的內容以 id 值爲 key 緩存到 $templateCache 服務中,此時能夠設置 templateUrl: 'Hello.html'

  • 一個函數,接收兩個參數 tElement(元素自己) 和 tAttrs(元素的屬性集合),返回 url 地址。

transclude(Boolean)

官方文檔的解釋爲:編譯元素的內容,使其在指令內部可用。該選項通常和 ng-transclude 指令一塊兒使用。

若是 transclude 設置爲 true,則元素的內容會被放到模板中設置了 ng-transclude 指令的元素中。例如:

app.directive('testTransclude', [
    function () {
        return {
            restrict: 'E',
            transclude: true,
            template:
                '<div>\
                    <p>指令內部段落</p>\
                    <div ng-transclude></div>\
                </div>'
        };
    }
]);
<test-transclude>
    <p>該段落會被放到指令內部</p>
</test-transclude>

上面生成後的 DOM 結構爲:

transclude執行結果

restrict(String)

指令的使用形式。

值能夠爲:

  • 'E' - 指令做爲元素使用

  • 'A' - 指令做爲屬性使用

  • 'C' - 指令做爲類名使用

  • 'M' - 指令做爲註釋使用(不經常使用)

能夠是以上值的組合,如 restrict: 'EA' 表示指令既能夠做爲屬性使用,也能夠做爲元素使用。

scope(Boolean or Object)

關於 scope 選項將會在後面的指令 scope 中細說。

controller(Function)

通常狀況下不須要使用指令的 controller,只要使用 link 就夠了,後面會細說 link 函數。

controller 的場景是該指令(a)會被其餘指令(b)require 的時候,在 b 的指令裏能夠傳入 a 的這個 controller,目的是爲了指令間的複用和交流。而 link 只能在指令內部中定義行爲,沒法作到這樣。

controllerAs(String)

爲控制器指定別名,這樣能夠在須要控制器的地方使用該名字進行注入。

require(String or Array)

表示指令依賴於一個或多個指令,並注入所依賴指令的控制器到 link 函數的第四個參數中。若是所依賴的指令不存在,或所依賴指令的控制器不存在則會報錯。

依賴名稱前綴能夠爲:

  • (沒有前綴) - 在當前元素中查找依賴指令的控制器,若是不存在則報錯。

  • ? - 在當前元素中查找依賴指令的控制器,若是不存在傳 nulllink 中。

  • ^ - 在當前元素及父元素中查找依賴指令的控制器,若是不存在則報錯。

  • ?^ - 在當前元素及父元素中查找依賴指令的控制器,若是不存在傳 nulllink 中。

例子:

app.directive('validate', [
    function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, ele, attrs, ngModelCtrl) {
                // 監聽值變化
                ngModelCtrl.$viewChangeListeners.push(function () {
                    scope.validateResult = ngModelCtrl.$viewValue === 'Heron';
                });
            }
        };
    }
]);

app.controller('myCtrl', [
    '$scope',
    '$cookieStore',
    function ($scope, $cookieStore) {
        $scope.name = 'Heron';

        $scope.sayHi = function (name, age) {
            alert('Hello ' + name + ', your age is ' + age);
        }
    }
]);
<div ng-controller="myCtrl">
    <input type="text" ng-model="name" validate>
    <p>
        validate 結果:{{validateResult}}
    </p>
</div>

運行結果如圖:

require運行結果

compile(Function)link(Function)

建立的建立過程能夠分爲編譯(compile)階段和連接(link)階段,所以二者放一塊兒講。

二者區別在於:

  • compile 函數的做用是對指令的模板進行轉換。

  • link 函數的做用是在視圖和模型之間創建關聯,包括註冊事件監聽函數和更新 DOM 操做。

  • scope 在連接階段纔會被綁定到元素上,所以 compile 函數中沒有入參 scope

  • 對於同一個指令的多個示例,compile 函數只會執行一次,而 link 函數在每一個實例中都會執行。

  • 若是自定義了 compile 函數,則自定義的 link 函數 無效,而是使用 compile 函數 返回的 link 函數。

指令 scope

scope 選項有三種值:

  • false - 使用父 scope。改變父 scope 會影響指令 scope,反之亦然。

  • true - 繼承父 scope,並建立本身的 scope。改變父 scope 會影響指令 scope,而改變指令 scope 不會影響父 scope

  • {} - 不繼承父 scope,建立獨立的 scope。若是不使用雙向綁定策略(後面會講),改變父 scope 不會影響指令 scope,反之亦然。

例子:

app.controller('myCtrl', [
    '$scope',
    '$cookieStore',
    function ($scope, $cookieStore) {
        $scope.scopeFalse = 'Heron';
        $scope.scopeTrue = 'Heron';
        $scope.scopeObject = 'Heron';
    }
]);
 
app.directive('directiveFalse', [
    function () {
        return {
            restrict: 'EA',
            scope: false,
            template: 
                '<div>\
                    <p>\
                        <span>指令 scope: </span>\
                        <input type="text" ng-model="scopeFalse">\
                    </p>\
                </div>'
        };
    }
]);
app.directive('directiveTrue', [
    function () {
        return {
            restrict: 'EA',
            scope: true,
            template: 
                '<div>\
                    <p>\
                        <span>指令 scope: </span>\
                        <input type="text" ng-model="scopeTrue">\
                    </p>\
                </div>'
        };
    }
]);
app.directive('directiveObject', [
    function () {
        return {
            restrict: 'EA',
            scope: {},
            template: 
                '<div>\
                    <p>\
                        <span>指令 scope: </span>\
                        <input type="text" ng-model="scopeObject">\
                    </p>\
                </div>',
            link: function (scope) {
                // 因爲使用獨立scope,所以須要本身定義變量
                scope.scopeObject = 'Heron';
            }
        };
    }
]);
<div ng-controller="myCtrl">
    <h3>scope: false</h3>
    <p>
        <span>父 scope: </span>
        <input type="text" ng-model="scopeFalse">
    </p>
    <directive-false></directive-false>
    <h3>scope: true</h3>
    <p>
        <span>父 scope: </span>
        <input type="text" ng-model="scopeTrue">
    </p>
    <directive-true></directive-true>
    <h3>scope: {}</h3>
    <p>
        <span>父 scope: </span>
        <input type="text" ng-model="scopeObject">
    </p>
    <directive-object></directive-object>
</div>

運行結果如圖:

指令scope運行結果

針對獨立 scope,能夠經過在對象中聲明如何從外部傳入參數。有如下三種綁定策略:

  • @ - 使用 DOM 屬性值單項綁定到指令 scope 中。此時綁定的值老是一個字符串,由於 DOM 的屬性值是一個字符串。

    <div my-directive age="26"></div>
    
    scope: {
        age: '@'
    }
  • = - 在父 scope 和指令 scope 之間創建雙向綁定。

    <div my-directive age="age"></div>
    
    scope: {
        age: '='
    }
  • & - 使用父 scope 的上下文執行函數。通常用於綁定函數。

    <div my-directive sayHi="sayHi()"></div>
    
    scope: {
        sayHi: '&'
    }

綁定函數時,有時須要向指令外部傳遞參數,以下:

app.controller('myCtrl', [
    '$scope',
    '$cookieStore',
    function ($scope, $cookieStore) {
        $scope.name = 'Heron';

        $scope.sayHi = function (name, age) {
            alert('Hello ' + name + ', your age is ' + age);
        };
    }
]);

app.directive('myDirective', [
    function () {
        return {
            restrict: 'E',
            replace: true,
            scope: {
                clickMe: '&'
            },
            template: 
                '<div>\
                    <button class="btn btn-info" ng-click="clickMe({ age: age })">點我</button>\
                </div>',
            link: function (scope) {
                scope.age = 26;
            }
        };
    }
]);
<div ng-controller="myCtrl">
    <my-directive click-me="sayHi(name, age)"></my-directive>
</div>

運行結果如圖:

指令向外部傳遞參數

說明一下:首先聲明 clickMe: '&' 使用父 scope 的環境執行 clickMe 函數,而後在傳遞給指令時聲明 click-me="sayHi(name, age)",表示父 scope 的 sayHi 方法須要兩個參數,一個是 name,一個是 age,而後再指令中使用對象 {} 的方式向外傳遞參數,如 ng-click="clickMe({ age: age })",表示向指令外傳遞 age 參數,sayHi 方法從指令拿到 age 參數,再從本身的上下文中拿到 name 參數。

結語

AngularJS 指令的開發和使用變幻無窮,也有許多坑,但願你們留意,也但願你們能在評論區多多交流心得。

相關文章
相關標籤/搜索