您好,登錄后才能下訂單哦!
這篇文章主要介紹AngularJS自定義指令的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
前言
除了 AngularJS 內置的指令外,我們還可以創建自定義指令。
通過 .directive() 函數來添加自定義的指令。
調用自定義指令時,需要在HTMl 元素上添加自定義指令名。
自定義指令命名規則:使用駝峰命名法來命名,即除第一個單詞外的首字母需大寫。如: myDirective。
在html頁面調用該指令時需要以 - 分割,如: my-directive。示例代碼:
<body ng-app="myApp"> <my-directive></my-directive> <script> var app = angular.module("myApp", []); app.directive("myDirective", function() { return { template : "<h2>模板:可以寫自己的html頁面代碼</h2>" }; }); </script> </body>
html頁面調用自定義指令的四種方式
通過在自定義指令里添加 restrict 屬性,根據設置不同的值來決定html頁面的調用方式,如:
var app = angular.module("myApp", []); app.directive("myDirective", function() { return { restrict : "A",//只能通過屬性調用 template : "<h2>自定義指令!</h2>" }; });
restrict值的不同,決定了調用方式的不同
屬性值 | 調用方式 | 示例 |
---|---|---|
A (Attribute首字母) | 屬性名 | <div my-directive></div> |
C (Class 首字母) | 類名 | <div class='my-directive'></div> |
E (Element 首字母) | 元素名 | <my-directive></my-directive> |
M | 注釋 | <!-- 指令: my-directive> |
restrict 默認值為 EA, 即在html頁面可通過元素名和屬性名來調用自定義指令。
自定義指令屬性詳解
屬性 | 值類型 | 說明 |
---|---|---|
restrict | string | 指令的調用方式,A、C、E、M |
priority | number | 指令執行的優先級 |
template | string | 指令使用的模板,可將html頁面代碼寫于此。只能與templateUrl二選其一 |
templateUrl | string | 從指定的url地址加載模板。只能與template二選其一 |
replace | boolean | 是否用模板替換當前元素。true : 將指令標簽替換成temple中定義的內容,頁面上不會再有<my-directive>標簽;false :則append(追加)在當前元素上,即模板的內容包在<my-directive>標簽內部。默認false。 |
transclude | boolean | 是否將當前元素的內容轉移到模板中 |
scope | boolean /object | 指定指令的作用域。false(默認值): 使用父作用域作為自己的作用域(每個引用自定義指令的標簽若其中一個標簽改變某一變量值,則會影響其他標簽的值 )。true: 新建一個作用域,該作用域繼承父作用域(兩個引用自定義指令的標簽之間的變量互不影響)。JavaScript對象:與父作用域隔離,并指定可以從父作用域訪問的變量 |
controller | function | 定義與其他指令進行交互的接口函數 |
require | string | 指定需要依賴的其他指令 |
link | function | 以編程的方式操作DOM,包括添加監聽器等 |
compile | function | 編程的方式修改DOM模板的副本,可以返回鏈接函數 |
對表格里的知識進行延伸
1.templateUrl
如果template里拼寫的html頁面代碼十分的多頁復雜,拼字符串的話就太麻煩啦,這里我們就可以選擇templateUrl。我們可以將要拼寫的html頁面代碼獨立到一個頁面里,如template.html;然后再指定該html文件所在的路徑即可,如templateUrl:”template.html”。用到該自定義指令時,會自動發一個http請求來獲取template.html對應的模板內容。這樣做的缺點是,多了一個http請求。別急,可以改進的:
angularjs規定了模板還可以用<Script>標簽定義:
<script type="text/ng-template" id="template.html"> <div>自定義指令模板用Script標簽定義的方式,須放在html頁面ng-controller指令所在標簽的內部</div> </script>
上面代碼寫在html頁面的ng-controller指令所在標簽的里面,這樣就不用再去請求它了。示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script> </head> <body > <div ng-app="myApp" ng-controller="myController"> <!-- 引用自定義指令 --> <my-directive></my-directive> <!-- 模板代碼:須放在myController所在標簽內部 --> <script type="text/ng-template" id="template.html"> <div> 自定義指令模板的templateUrl形式</div> </script> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { }); //創建自定義指令 app.directive("myDirective", function() { return { restrict:'E', templateUrl : "template.html" }; }); </script> </body> </html>
有多個模板時,我們可以將所有的模板集中在一個文件中,只需加載一次,然后根據id的不同調用不同的模板。
2.transclude
定義是否將當前元素(html頁面的自定義指令)的內容轉移到模板中。
模板中要接收當前元素內容的標簽需要使用ng-transclude指令。
<body > <div ng-app="myApp" ng-controller="myController"> <!-- 引用自定義指令 --> <my-directive>自定義指定令內容</my-directive> <!-- 模板代碼 --> <script type="text/ng-template" id="template.html"> <div> 模板內容</div> <div ng-transclude></div>//模板接收上面自定義指令間的內容 </script> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { }); //創建自定義指令 app.directive("myDirective", function() { return { templateUrl : "template.html", transclude : true//轉移到模板中 }; }); </script> </body>
3.什么是scope的父作用域
引用自定義指令的html頁面的控制器所能控制的范圍。下面代碼的父作用域就是myController所控制的范圍
<body > <div ng-app="myApp" ng-controller="myController"> <my-directive></my-directive><!-- 引用自定義指令 --> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope){ }); //創建自定義指令 app.directive("myDirective", function() { return { template : "<h2>自定義指令!</h2>" }; }); </script> </body>
4.scope屬性的值是對象(object)時的用法
AngularJS內置指令的用法:ng-model=”obj”,通過obj這個變量雙向綁定值,controller里變了,html頁面也跟著變化。這說明,內置指令不僅可作為屬性,還可動態改變值,這個要是不懂的,看看基礎語法。如下代碼:
<div ng-app="myApp" ng-controller="myController"> 要動態變化的內容: <input ng-model="obj"> </div> <script> var app = angular.module('myApp', []); app.controller('myController', function($scope) { $scope.obj = "這個字符串值會同步到html里"; }); </script>
自定義指令當然也需要實現這種功能啦。scope屬性為對象時,可為自定義指令指定一個可以綁定值的屬性。這里還得說明一下的是,這個scope屬性與自定義指令里link屬性里的scope參數是一個變量。
<!-- =符號的用法--> <body > <div ng-app="myApp" ng-controller="myController"> <!-- 引用自定義指令:obj變量與控制器里的objc變量雙向綁定了值 --> <my-directive speak="obj"></my-directive> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { $scope.obj="父作用域";//父作用域給自定義指令屬性賦的值 }); //創建自定義指令 app.directive("myDirective", function() { return { template : "<p>模板內容</p>", scope:{ title:"=speak"//定義一個speak屬性供html頁面的自定義指令用。如果寫成title:"="格式,則自定義指令里的屬性名就是title。 }, link: function postLink(scope, iElement, iAttrs) { console.log(scope.title)//這里打印的值與控制器里的值一樣 } }; }); </script> </body>
有了前面的一個示例,下面再來說說綁定策略:即用符號前綴給自定義指令傳值。它是一個鍵值對,鍵是在自定義指令中使用的,值里符號后面的字符串是html頁面自定義指令的屬性名;如果值里只有符號,則html頁面自定義指令的屬性名就是鍵名。
符號 | 說明 | 示例 |
---|---|---|
@ | 值傳遞,單向綁定。html頁面自定義指令里的val屬性的值可傳給link的scope使用。第一種寫法——str : “@”,這種寫法html頁面的指令屬性名為str | str : “@val”,屬性名為val |
= | 雙向綁定數據到指令的屬性中,數據值可以是任意類型的。第一種寫法:name : “=”,這種寫法html頁面的自定義指令屬性名就是name | name : “=username”,屬性名是username |
& | 使用父作用域中的一個函數,可以在指令中調用。第一種寫法:getName:”&”,這種寫法html頁面的自定義指令屬性名就是gegName | getName : “&getUserName”,屬性名是getUserName |
其余兩種符號用法:
<!-- @符號的用法 --> <body > <div ng-app="myApp" ng-controller="myController"> <!-- 引用自定義指令 --> <my-directive title="obj" str="abcd">自定義指定令的內容555</my-directive> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { $scope.obj="父作用域";//父作用域給自定義指令屬性賦的值 }); //創建自定義指令 app.directive("myDirective", function() { return { template : "<p >模板內容</p>", scope:{ title:"=", str:"@" }, link: function postLink(scope, iElement, iAttrs) { console.log(scope.str) console.log(scope.title) } }; }); </script> </body>
<!-- &符號的用法 --> <body > <div ng-app="myApp" ng-controller="myController"> <!-- 引用自定義指令 --> <my-directive fun="test()"></my-directive> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { $scope.test = function(){ console.log('自定義指令會調用該法,所以這句話會打印到控制臺上') } }); //創建自定義指令 app.directive("myDirective", function() { return { template : "<p >模板內容</p>", scope:{ fun:"&"//屬性名直接是fun }, link: function postLink(scope, iElement, iAttrs) { scope.fun();//調用父作用域的方法,好似不能傳參,未深究。 } }; }); </script> </body>
5.controller屬性
controller屬性用于提供對外的接口,即該自定義指令會被其他自定義指令調用。所謂的接口,就是this后的變量或方法。
controller可以使用的參數,作用域、節點、節點的屬性、節點內容的遷移,這些都可以通過依賴注入被傳進來,所以你可以根據需要只寫要用的參數,有$scope,Z$element, $attrs, $transclude。
調用該自定義指令的指令需要放在該指令之間。假定firstDirective指令是要被調用的自定義指令,expander是調用者指令。如下:
<first-directive> <expander ng-repeat="item in list" attribute="list">{{item.title}}:{{item.text}}</expander> </first-directive>
既然firstDirective內部還有指令,則firstDirective必須配置transclude屬性為true。代碼如下:
//用于被調用的自定義指令 app.directive('firstDirective',function(){ return { template : '<div ng-transclude></div>', replace : true, transclude : true, controller :function(){ this.getData = function(val){ var data = 3 * val; return data; } this.a = "abc"; } } });
調用其他指令的自定義指令必須配置require屬性指定指令名。然后在link函數里就可注入要調用的指令。
//自定義指令 app.directive('expander',function(){ return { templateUrl : 'template.html', replace : true, transclude : true, require : '^?firstDirective',//引用其他自定義指令,^表示從父節點開始找,?表示將告訴$compile服務,如果所需的指令沒找到,不要拋出異常 scope : { title : '=attribute' }, link : function(scope,element,attris,firstDirct){//注入 console.log(firstDirct.a)//調用其他指令的變量 console.log(firstDirct.getData(6)) //調用其他指令的方法 } }; });
6.link屬性用法
link后的方法在指令中負責執行DOM 操作和注冊事件監聽器等。link函數有五個參數(scope,element,attrs,controller,linker)。link 方法的參數解釋:
scope: 它與自定義指令里的scope屬性是一個東西。它是指令scope的引用,所以可改名為sco等其他名字。scope 變量在初始化時是不被定義的,link 方法會注冊監視器監視值變化事件。
element: 包含指令的DOM元素的引用, link 方法一般通過jQuery 操作實例(如果沒有加載jquery,還可以使用Angular's jqLite )。
controller: 在有嵌套指令的情況下使用。這個參數作用在于把子指令的引用提供給父指令,允許指令之間進行交互,如前面的例子。
注意:當調用link 方法時, 通過值傳遞(”@”)的scope 變量將不會被初始化,它們將會在指令的生命周期中另一個時間點進行初始化,如果你需要監聽這個事件,可以使用scope.$watch 方法。
7.link與compile的區別
compile函數有三個參數(cElement,cAttrs,cLinker),使用compile函數可以在ng創建原始dom實例以及創建scope實例之前,改變原始的dom(template element);可以應用于當需要生成多個element實例但只有一個template element的情況,ng-repeat就是一個最好的例子。它就在是compile函數階段改變原始的dom生成多個原始dom節點,然后每個又生成element實例。因為compile只會運行一次,所以當你需要生成多個element實例的時候是可以提高性能的。
link函數有五個參數(scope,element,attrs,ctrl,linker)。
link又分為pre-link和post-link,在代碼里直接用pre和post表示,當我們直接使用link時,默認跟post一樣。我在網上找了個例子來說明一下區別,代碼如下:
<body> <div ng-app="myApp" ng-controller="myController"> <level-one> <level-two> <level-three> Hello </level-three> </level-two> </level-one> </div> <script> //創建模塊 var app = angular.module('myApp', []); //創建控制器 app.controller('myController', function($scope) { }); //自定義指令 function createDirective(name){ return function(){ return { restrict: 'E', compile: function(tElem, tAttrs){ console.log(name + ': compile => ' + tElem.html()); return { pre: function(scope, iElem, iAttrs){ console.log(name + ': pre link => ' + iElem.html()); }, post: function(scope, iElem, iAttrs){ console.log(name + ': post link => ' + iElem.html()); } } } } } } app.directive('levelOne', createDirective('levelOne')); app.directive('levelTwo', createDirective('levelTwo')); app.directive('levelThree', createDirective('levelThree')); </script> </body>
注意打印結果:
levelOne: compile => <level-two> <level-three> Hello </level-three> </level-two> levelTwo: compile => <level-three> Hello </level-three> levelThree: compile => Hello levelOne: pre link => <level-two> <level-three> Hello </level-three> </level-two> levelTwo: pre link => <level-three> Hello </level-three> levelThree: pre link => Hello levelThree: post link => Hello levelTwo: post link => <level-three> Hello </level-three> levelOne: post link => <level-two> <level-three> Hello </level-three> </level-two>
分析打印結果:
運行levelone指令中的compile函數,ng就會遞歸遍歷它的dom節點,然后在level-two與level-three上面重復這些操作。所以會依次打印連續三個compile。
pre會在所有compile執行完后且在所有post之前執行。這樣可以在執行post前執行一些其他代碼,有些類似AOP。
由上面結果可知,post的執行順序卻是先levelthree最后levelone,即反向調用相關聯的post-link函數。這么做的好處是,當我們運行levelone時,保證leveltwo與levelthree都已經執行過了,這樣就會更安全。所以默認的link就是post。
一個我做過的分頁例子
之所以展示這個代碼,主要是給一些朋友看看真實的項目,,多余的東西刪掉了,具體的注入這里就不在講了。
html頁面代碼:
<div class="wp-20" ng-controller="AppStatisticController" ng-cloak> <div class="panel-footer"> <s-pagination conf="paginationConf"></s-pagination> </div> </div>
控制器代碼:
"use strict";//嚴格 define(["application-configuration", "s-pagination", "tableDataService"], function (app) { app.register.controller("AppStatisticController", ["$scope", "$rootScope", "$stateParams","$http", "tableDataService", function($scope, $rootScope, $stateParams, $http, tableDataService) { var getTableDataSuccess = function(result) { if(result.c == 1) { $scope.title = result.title; $scope.lists = result.pageList; $scope.total = result.data; $scope.paginationConf.totalItems = result.total; }else if(result.c == 2){ //彈出框,沒有查到數據 } else { alert(result.i); } }; var getTableDataError = function(result) { alert(result); }; /*重要的代碼,這個paginationConf與自定義指令雙向綁定數據*/ $scope.paginationConf = { currentPage: 1, itemsPerPage: 10, pagesLength: 9, search: false, onChange: function() { var param = { "pageNo": this.currentPage, "pageSize": this.itemsPerPage, "timeType": $scope.formData.timeType, "adStyle":$scope.formData.adStyle, }; param.appId = $stateParams.appId; tableDataService.getTableData( param, "ims/appStat.do", getTableDataSuccess, getTableDataError ); } }; $scope.$watch("formData",function(newValue,oldValue, scope) { if(newValue.keywords == oldValue.keywords) { $scope.paginationConf.search = true; } }, true); }]); });
自定義指令代碼:也算是angularJS的分頁插件
/** * 分頁插件封裝s-pagination.js * @date 2016-05-06 * @author Peter */ angular.module('s.pagination', []).directive('sPagination',[function(){//自定義指令 return { restrict: 'E',//僅限元素名調用 template: '<div class="page-list">' + '<ul class="pagination" ng-show="conf.totalItems > 0">' + '<li ng-class="{disabled: conf.currentPage == 1}" ng-click="prevPage()"><span>«</span></li>' + '<li ng-repeat="item in pageList track by $index" ng-class="{active: item == conf.currentPage, separate: item == \'...\'}" ' + 'ng-click="changeCurrentPage(item)">' + '<span>{{ item }}</span>' + '</li>' + '<li ng-class="{disabled: conf.currentPage == conf.numberOfPages}" ng-click="nextPage()"><span>»</span></li>' + '</ul>' + '<div class="page-total" ng-show="conf.totalItems > 0">' + '第<input type="text" ng-model="jumpPageNum" ng-keyup="jumpToPage($event)"/>頁 ' + '每頁<select ng-model="conf.itemsPerPage" ng-options="option for option in conf.perPageOptions "></select>' + '/共<strong>{{ conf.totalItems }}</strong>條' + '</div>' + '<div class="no-items" ng-show="conf.totalItems <= 0">暫無數據</div>' + '</div>', replace: true, scope: { conf: '='//雙向綁定數據 }, link: function(scope, element, attrs){ // 變更當前頁 scope.changeCurrentPage = function(item) { if(item == '...'){ return; }else{ scope.conf.currentPage = item; } }; // 定義分頁的長度必須為奇數 (default:5) scope.conf.pagesLength = parseInt(scope.conf.pagesLength) ? parseInt(scope.conf.pagesLength) : 5 ; if(scope.conf.pagesLength % 2 === 0){ // 如果不是奇數的時候處理一下 scope.conf.pagesLength = scope.conf.pagesLength -1; } // conf.erPageOptions if(!scope.conf.perPageOptions){ scope.conf.perPageOptions = [10, 20, 30, 40, 50]; } // pageList數組 function getPagination(newValue, oldValue) { //新增屬性search 用于附加搜索條件改變時觸發 if(newValue[1] != oldValue[1] || newValue[2] != oldValue[2]) { scope.conf.search = true; } // conf.currentPage scope.conf.currentPage = parseInt(scope.conf.currentPage) ? parseInt(scope.conf.currentPage) : 1; // conf.totalItems scope.conf.totalItems = parseInt(scope.conf.totalItems) ? parseInt(scope.conf.totalItems) : 0; // conf.itemsPerPage (default:15) scope.conf.itemsPerPage = parseInt(scope.conf.itemsPerPage) ? parseInt(scope.conf.itemsPerPage) : 15; // numberOfPages scope.conf.numberOfPages = Math.ceil(scope.conf.totalItems/scope.conf.itemsPerPage); // judge currentPage > scope.numberOfPages if(scope.conf.currentPage < 1){ scope.conf.currentPage = 1; } // 如果分頁總數>0,并且當前頁大于分頁總數 if(scope.conf.numberOfPages > 0 && scope.conf.currentPage > scope.conf.numberOfPages){ scope.conf.currentPage = scope.conf.numberOfPages; } // jumpPageNum scope.jumpPageNum = scope.conf.currentPage; // 如果itemsPerPage在不在perPageOptions數組中,就把itemsPerPage加入這個數組中 var perPageOptionsLength = scope.conf.perPageOptions.length; // 定義狀態 var perPageOptionsStatus; for(var i = 0; i < perPageOptionsLength; i++){ if(scope.conf.perPageOptions[i] == scope.conf.itemsPerPage){ perPageOptionsStatus = true; } } // 如果itemsPerPage在不在perPageOptions數組中,就把itemsPerPage加入這個數組中 if(!perPageOptionsStatus){ scope.conf.perPageOptions.push(scope.conf.itemsPerPage); } // 對選項進行sort scope.conf.perPageOptions.sort(function(a, b){return a-b}); scope.pageList = []; if(scope.conf.numberOfPages <= scope.conf.pagesLength){ // 判斷總頁數如果小于等于分頁的長度,若小于則直接顯示 for(i =1; i <= scope.conf.numberOfPages; i++){ scope.pageList.push(i); } }else{ // 總頁數大于分頁長度(此時分為三種情況:1.左邊沒有...2.右邊沒有...3.左右都有...) // 計算中心偏移量 var offset = (scope.conf.pagesLength - 1)/2; if(scope.conf.currentPage <= offset){ // 左邊沒有... for(i =1; i <= offset +1; i++){ scope.pageList.push(i); } scope.pageList.push('...'); scope.pageList.push(scope.conf.numberOfPages); }else if(scope.conf.currentPage > scope.conf.numberOfPages - offset){ scope.pageList.push(1); scope.pageList.push('...'); for(i = offset + 1; i >= 1; i--){ scope.pageList.push(scope.conf.numberOfPages - i); } scope.pageList.push(scope.conf.numberOfPages); }else{ // 最后一種情況,兩邊都有... scope.pageList.push(1); scope.pageList.push('...'); for(i = Math.ceil(offset/2) ; i >= 1; i--){ scope.pageList.push(scope.conf.currentPage - i); } scope.pageList.push(scope.conf.currentPage); for(i = 1; i <= offset/2; i++){ scope.pageList.push(scope.conf.currentPage + i); } scope.pageList.push('...'); scope.pageList.push(scope.conf.numberOfPages); } } if(scope.conf.onChange){ //請求數據 if(scope.conf.search) { scope.conf.onChange(); scope.conf.search = false; } } scope.$parent.conf = scope.conf; } // prevPage scope.prevPage = function(){ if(scope.conf.currentPage > 1){ scope.conf.currentPage -= 1; } }; // nextPage scope.nextPage = function(){ if(scope.conf.currentPage < scope.conf.numberOfPages){ scope.conf.currentPage += 1; } }; // 跳轉頁 scope.jumpToPage = function(){ scope.jumpPageNum = scope.jumpPageNum.replace(/[^0-9]/g,''); if(scope.jumpPageNum !== ''){ scope.conf.currentPage = scope.jumpPageNum; } }; scope.$watch(function() { if(!scope.conf.totalItems) { scope.conf.totalItems = 0; } if(angular.isUndefined(scope.conf.search)) { scope.conf.search = false; } var newValue = [scope.conf.totalItems, scope.conf.currentPage, scope.conf.itemsPerPage, scope.conf.search]; return newValue; }, getPagination, true); } }; }]);
以上是“AngularJS自定義指令的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。