4 回答

TA貢獻1909條經驗 獲得超7個贊
在本文中,我們將一步一步學著創建一個用來輸入時間值的指令。我們的目標是能夠將一個普通的文本輸入字段變成一個能夠以HH:mm格式顯示時間的時間選擇器,但是將它以毫秒為單位存儲在我們的數據屬性中。
對于這個例子,我們的HTML非常簡單,如下所示:
<input type='text' ng-model='timeOfDay' time-picker/>
顯然,我們需要在主頁中包含一個timePicker插件以便于它能夠被綁定在我們的input上。對于這個指令,我們首先來進行一個最基本的定義,由于我們需要數據綁定功能,我們將從一開始就require ngModel:
.directive('timePicker',function(){ var today = new Date(new Date().toString()); return { require: '?ngModel', link: function($scope,$element,$attrs,ngModel){ } }});
注意到我們需要涉及元素的實際實例,因此我們所有的代碼基本上都位于linking函數中,我們在此完全不需要擔心編譯過程。我們同事也實例化了一個today變量來保存一個Date對象,以此在午夜十分更新。創建這個變量作為factory函數的一部分允許我們在多個實例之間分享一個today變量。注意,盡管這允許我們將內存的使用最小化,它也意味著如果我們的應用在半夜還依然再運行,我們的指令會開始提供不準確的結果。如果你打算將這個指令用于一個實時應用中,你最好創建另一個函數在第二天來臨時更新這個值。現在,我們急需要看我們的controller函數:
link: function($scope.$element,$attrs,ngModel){ ngModel = ngModel || { "$setViewValue" : angular.noop }}
你以前可能也注意過這種模式。一定要記住我們讓我們的controller有進行選擇性的需求,因此如果有人想要使用我們的指令來綁定一個timepicker,而不需要由ngModel提供一個數據綁定,當指令沒找到一個請求的controller時它也不會拋出一個錯誤。在這里,如果ngModel被定義了,我們就使用它,否則,我們就使用自定義的那個對象,其中有一個noop函數。于是當我們在指令中調用ngModel.$setViewValue時,如果沒有ng-Model綁定到我們的節點上,我們的代碼會繼續執行。
說到ngModel.$setViewValue,我們來看看我們如何綁定我們的time picker,以及我們在什么地方會需要這個函數:
link: function($scope,$element,$attrs,ngModel){ .. var initialized = false; setTimeout(function(){ initialized = $element.timepicker() .on('changeTime',function(ev,ui){ var sec = $element.timepicker('getSecondsFrimMidnight'); ngModel.$setViewValue(sec * 1000); }); });}
毫無疑問的,你的第一個問題肯定是關于setTimeout的,因為它其中居然沒有實際的延時數字。因為我們處于linking函數中,我們的$element是完全被實例化的,因此這種小技巧是沒有必要的。但是我們還是推薦使用這種辦法。
現在我們來看看我們如何使用$setViewValue。因為我們之前已經獲得了一個分享的ngModel的實例,我們現在可以在我們的指令中調用$setViewValue函數,它將幫助我們鏈接我們的插件和數據模型。記住這是用來接受顯示值的,它可以進行任何必要的解析,然后將它儲存在數據屬性中。timepicker插件將會發送一個changeTime時間,只要用戶更新了在我們的input中顯示的時間,因此我們使用它來了解應該何時改變內部值。在我們的時間處理函數內部,我們只需要獲得自午夜以來的秒數,對此插件提供了一個方便的方法,然后將它乘以1000并傳遞給$setViewValue。當我們完成了以上過程,我們的數據將會經歷以下的步驟:
一旦我們將我們的timepicker實例化完成并監聽視圖上的變化,我們的下一步就是要定義$render方法,它將負責將數據值轉換為合適的顯示或者視圖值。只要我們指令內部的數值發生變化,它就會發生,包括第一次實例化時:
link : function ($scope, $element, $attrs, ngModel) { ... ngModel.$render = function (val) { if (!initialized) { //如果$render在我們的timepicker插件準備好之前就被調用,返回 return; }; $element.timepicker('setTime', new Date(today.getTime() + val)); }}
再次,注意到我們實際上重定義了$render方法,因此當ngModel觀察到一個數據變化時,它告訴控制器來執行$render,這時我們定義的$render就被調用了。我們所需要做的僅僅是將數據模型的值轉換成為我們的插件期待的值。在這個例子中,timepicker插件提供了一種方法來設置時間顯示。因為我們的時間以毫秒存儲,當我們需要渲染一個值時,我們只需要簡單地從today獲取時間,加上我們的新值,然后用這個值創建一個Date對象即可。當我們完成時,我們的數據將會通過以下步驟返回到視圖中:

TA貢獻1796條經驗 獲得超4個贊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <!doctype html> <html> <head> <meta charset="utf-8"> <title>test</title> <script src="angular.min.js"></script> <script> var myApp = angular.module("myApp", []); myApp.directive('testIt',function(){ return { restrict: 'A', scope: false, link:function(scope, elm, attr){ if(scope.$last){ elm.css('color','red'); scope.myFunction(); } } } }); myApp.controller("testCtrl", function($scope){ $scope.items = [0,1,2,3,4]; $scope.myFunction = function(){ console.log('Hello!'); }; }); </script> </head>
<body> <div ng-app="myApp"> <div ng-controller="testCtrl"> <ul> <li ng-repeat="item in items" test-it >{{item}}</li> </ul> </div> </div> </body> </html> |
主要在于12行的scope:false,這個是默認的,其實你不寫也是false。這樣drective繼承了父scope,所以可以調用父作用域的方法,而聲明新的scope即scope:{}形式就不會繼承了,不過你依然可以用scope.$parent.myFunction()的方式調用。

TA貢獻1801條經驗 獲得超16個贊
正如很多文章所說的一樣,指令是AngularJS的靈魂,只有真正熟練掌握了Angular 指令,才有希望成為AngularJS高手。在編寫Angular指令時,我們最常用到的就是其中的link函數,它用來與scope中的數據進行交互并綁定一些事件,例如click等等。但是如果我們想要編寫一些高級的指令,例如要在指令之間進行相互交流的指令,我們就必須用到Angular指令中的controller。
指令中的controller是一個難點,我們也很少會用到它。一般來說,我們最常用到需要進行交互的指令就是ngModel。如果你想要創建一個自定義input,或者甚至是想綁定一個需要制定格式的input插件,ngModel都會為你提供你所需要的方法,它會幫助你在你的數據模型和插件之間進行交互。
在本文中,我們將一步一步學著創建一個用來輸入時間值的指令。我們的目標是能夠將一個普通的文本輸入字段變成一個能夠以HH:mm格式顯示時間的時間選擇器,但是將它以毫秒為單位存儲在我們的數據屬性中。
- 4 回答
- 0 關注
- 596 瀏覽
添加回答
舉報