AngularJs自訂指令詳解(5) - link

來源:互聯網
上載者:User

標籤:tle   自訂指令   輸出時間   rootscope   雙向   order   定時   watch   ima   

在指令中操作DOM,我們需要link參數,這參數要求聲明一個函數,稱之為連結函數。

寫法:

link: function(scope, element, attrs) {
  // 在這裡操作DOM
}

如果指令使用了require選項,那麼連結函數會有第四個參數,代表控制器或者所依賴的指令的控制器。

// require ‘SomeController‘,
link: function(scope, element, attrs, SomeController) {
  // 在這裡操作DOM,可以訪問required指定的控制器
}

連結函數之所以能夠在指令中操作DOM,就是因為傳入的這幾個參數:scope、element、attrs

scope:即與指令元素相關聯的當前範圍,可以用來註冊監聽器:scope.$watch()

element:即當前指令對應的元素,使用它可以操作該元素及其子項目。例如<span my-directive></span>,這個span就是指令 my-directive所使用的元素。

attrs:由當前元素的屬性群組成的對象。

下面看一個例子,來自官方文檔的樣本。

我們要實現一個時鐘,根據給定的時間格式顯示當前的時間,而且每隔一秒要更新一次時間。

首先在控制器中初始化一個時間格式:

controller(‘Controller‘, [‘$scope‘, function($scope) {
    $scope.format = ‘M/d/yy h:mm:ss a‘;
}])

對於時間格式,顯然我們要引入$filter服務。

對於”每隔一秒“進行某些操作,顯然要引入$interval服務。

為了測試程式,我們還引入$log服務以便在瀏覽器中觀察輸出。

所以自訂的指令需要寫成這樣:

directive(‘myClock‘, [‘$interval‘, ‘$filter‘, ‘$log‘, function($interval, $filter,$log)

這個myClock指令將會被注入$interval、$filter、$log服務。

重新整理時間顯示,也就是要求我們在指令中操作DOM,輸出時間:

function updateTime() {
  element.text($filter(‘date‘)(new Date(), interFormat));
}

$filter方法的使用:

$filter(‘date‘)(date, format, timezone)

參考https://code.angularjs.org/1.3.16/docs/api/ng/filter/date

每隔一秒重新整理顯示:

$interval(

  function() {
    updateTime();
  },

  1000

);

完整代碼如下:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘docsTimeDirective‘, [])                .controller(‘Controller‘, [‘$scope‘, function($scope) {                    $scope.format = ‘M/d/yy h:mm:ss a‘;                }])                .directive(‘myClock‘, [‘$interval‘, ‘$filter‘, ‘$log‘, function($interval, $filter,$log) {                    return {                        scope:{                            myFormat:‘=‘                        },                        link: function(scope, element, attrs) {                            function updateTime() {                                element.text($filter(‘date‘)(new Date(), scope.myFormat));                            }                            updateTime();                            $interval(function() {                                updateTime();                            }, 1000);                        }                    };                }]);    </script></head><body ng-app="docsTimeDirective"><div ng-controller="Controller">    時間格式: <input ng-model="format"> <hr/>    目前時間: <span my-clock my-format="format"></span></div></body></html>

運行效果:

不過我們很快就發現一個問題,就是修改時間格式後,無法立刻重新整理時間顯示,把每隔一秒修改為每隔五秒,問題就更加明顯了。

雖然修改format會因為雙向繫結而使myFormat發生變化,但是後者並不會觸發執行updateTime()函數。

所以需要加入$watch監聽:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘docsTimeDirective‘, [])                .controller(‘Controller‘, [‘$scope‘, function($scope) {                    $scope.format = ‘M/d/yy h:mm:ss a‘;                }])                .directive(‘myClock‘, [‘$interval‘, ‘$filter‘, ‘$log‘, function($interval, $filter,$log) {                    return {                        scope:{                            myFormat:‘=‘                        },                        link: function(scope, element, attrs) {                            function updateTime() {                                element.text($filter(‘date‘)(new Date(), scope.myFormat));                            }                            scope.$watch(‘myFormat‘,function(newValue) {                                $log.info(‘value changed to ‘ + newValue);                                updateTime();                             });                            $interval(function() {                                updateTime();                            }, 1000);                        }                    };                }]);    </script></head><body ng-app="docsTimeDirective"><div ng-controller="Controller">    時間格式: <input ng-model="format"> <hr/>    目前時間: <span my-clock my-format="format"></span></div></body></html>

注意$watch()的第一個參數為‘myFormat‘,不要少了單引號,也不要寫成‘scope.myFormat‘、‘$scope.myFormat‘,要不然newValue的值是undefined了。

還有就是刪掉了外面的updateTime()調用,因為在$watch裡myFormat第一次綁定時,已經觸發監聽器的回呼函數了,於是updateTime()也立刻執行。

上面的代碼監聽的是定義在指令的隔離範圍上的myFormat,而官方文檔監聽的是DOM中span元素的my-format屬性,效果是差不多的,代碼如下:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘docsTimeDirective‘, [])                .controller(‘Controller‘, [‘$scope‘, function($scope) {                    $scope.format = ‘M/d/yy h:mm:ss a‘;                }])                .directive(‘myClock‘, [‘$interval‘, ‘$filter‘, ‘$log‘, function($interval, $filter,$log) {                    return {                        link: function(scope, element, attrs) {                            var interFormat, timeoutId;                            function updateTime() {                                element.text($filter(‘date‘)(new Date(), interFormat));                            }                            scope.$watch(attrs.myFormat, function(value) {                                interFormat = value;                                updateTime();                            });                            element.on(‘$destroy‘, function() {                                $interval.cancel(timeoutId);                            });                            timeoutId = $interval(function() {                                updateTime();                            }, 1000);                        }                    };                }]);    </script></head><body ng-app="docsTimeDirective"><div ng-controller="Controller">    時間格式: <input ng-model="format"> <hr/>    Current time is: <span my-clock my-format="format"></span></div></body></html>

官方文檔指出一個問題:$interval註冊的匿名函數不會在元素被移除時自動釋放,存在一定的記憶體泄露風險,所以增加了代碼:

element.on(‘$destroy‘, function() {
  $interval.cancel(timeoutId);
});

這三行代碼也示範了如何在指令內加入對元素的事件監聽器,官方文檔還提供了另一個例子,代碼如下:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘dragModule‘, [])                .directive(‘myDraggable‘, [‘$document‘, function($document) {                    return {                        link: function(scope, element, attr) {                            var startX = 0, startY = 0, x = 0, y = 0;                            element.css({                                position: ‘relative‘,                                border: ‘1px solid blue‘,                                backgroundColor: ‘yellow‘,                                cursor: ‘pointer‘                            });                            element.on(‘mousedown‘, function(event) {                                // Prevent default dragging of selected content                                event.preventDefault();                                startX = event.pageX - x;                                startY = event.pageY - y;                                $document.on(‘mousemove‘, mousemove);                                $document.on(‘mouseup‘, mouseup);                            });                            function mousemove(event) {                                y = event.pageY - startY;                                x = event.pageX - startX;                                element.css({                                    top: y + ‘px‘,                                    left:  x + ‘px‘                                });                            }                            function mouseup() {                                $document.off(‘mousemove‘, mousemove);                                $document.off(‘mouseup‘, mouseup);                            }                        }                    };                }]);    </script></head><body ng-app="dragModule"><span my-draggable>Drag ME</span></body></html>

 現在回顧一下前面文章提到的隔離範圍問題,看看以下代碼:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘app‘,[])                .directive(‘myDirective‘,function(){                    return{                        template:‘Hello {{greeting}}!‘,                        //scope:{ },                        link:function(scope,element,attrs){                            scope.greeting = ‘AngularJs‘;                        }                    };                });    </script></head><body ng-app="app"><div ng-init="greeting=‘World‘"></div>1,<span>Hello {{greeting}}!</span><hr>2,<span my-directive></span><hr></body></html>

輸出:

1,Hello AngularJs!

2,Hello AngularJs!

雖然greeting被初始化為‘World‘,但是在連結函數裡修改成‘AngularJs’,可見此時傳給連結函數的scope是上一級範圍(在這裡是rootScope)

這造成了汙染,一般情況下我們不希望指令不聲不響地修改外面的變數,解決辦法是把代碼裡//scope:{}的注釋去掉,隔離指令的範圍。

於是輸出就會變成:

1,Hello World!

2,Hello AngularJs!

 

再看下面的代碼:

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <script src="../lib/angular-1.3.16/angular.min.js"></script>    <script src=""></script>    <title></title>    <script language="JavaScript">        angular.module(‘app‘,[])                .directive(‘myDirective‘,function(){                    return{                        restrict:‘E‘,                        template:‘<span ng-transclude></span>‘,                        scope:{ },                        transclude: true,                        link:function(scope,element,attrs){                            scope.greeting = ‘AngularJs‘;                        }                    };                });    </script></head><body ng-app="app"><div ng-init="greeting=‘World‘"></div>1,<span>Hello {{greeting}}!</span><hr>2,<my-directive>Hello {{greeting}}!</my-directive><hr></div></body></html>

輸出:

1,Hello World!

2,Hello World!

即使ng-transclude指令放在指令定義的模板中,但是{{greeting}}綁定放在外面,而指令已經隔離了範圍,所以{{greeting}}使用的是外面的‘World‘。

如果注釋掉scope:{},指令的範圍沒有隔離,於是輸出就變為:

1,Hello AngularJs!

2,Hello AngularJs!

 

AngularJs自訂指令詳解(5) - link

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.