AngularJS自定义指令详解(有分页插件代码)

前言

除了 AngularJS 内置的指令外,我们还可以创建自定义指令。

通过 .directive() 函数来添加自定义的指令。

调用自定义指令时,需要在HTMl 元素上添加自定义指令名。

自定义指令命名规则:使用驼峰命名法来命名,即除第一个单词外的首字母需大写。如: myDirective。

在html页面调用该指令时需要以 - 分割,如: my-directive。示例代码:








html页面调用自定义指令的四种方式

通过在自定义指令里添加 restrict 属性,根据设置不同的值来决定html页面的调用方式,如:

var app = angular.module("myApp", []);
app.directive("myDirective", function() {
 return {
  restrict : "A",//只能通过属性调用
  template : "

自定义指令!

" }; });

restrict值的不同,决定了调用方式的不同

属性值 调用方式 示例
A (Attribute首字母) 属性名
C (Class 首字母) 类名
E (Element 首字母) 元素名
M 注释

有多个模板时,我们可以将所有的模板集中在一个文件中,只需加载一次,然后根据id的不同调用不同的模板。

2.transclude

定义是否将当前元素(html页面的自定义指令)的内容转移到模板中。
模板中要接收当前元素内容的标签需要使用ng-transclude指令。


 
自定义指定令内容

3.什么是scope的父作用域

引用自定义指令的html页面的控制器所能控制的范围。下面代码的父作用域就是myController所控制的范围


 

4.scope属性的值是对象(object)时的用法

AngularJS内置指令的用法:ng-model=”obj”,通过obj这个变量双向绑定值,controller里变了,html页面也跟着变化。这说明,内置指令不仅可作为属性,还可动态改变值,这个要是不懂的,看看基础语法。如下代码:

要动态变化的内容:

自定义指令当然也需要实现这种功能啦。scope属性为对象时,可为自定义指令指定一个可以绑定值的属性。这里还得说明一下的是,这个scope属性与自定义指令里link属性里的scope参数是一个变量。



 

有了前面的一个示例,下面再来说说绑定策略:即用符号前缀给自定义指令传值。它是一个键值对,键是在自定义指令中使用的,值里符号后面的字符串是html页面自定义指令的属性名;如果值里只有符号,则html页面自定义指令的属性名就是键名。

符号 说明 示例
@ 值传递,单向绑定。html页面自定义指令里的val属性的值可传给link的scope使用。第一种写法――str : “@”,这种写法html页面的指令属性名为str str : “@val”,属性名为val
= 双向绑定数据到指令的属性中,数据值可以是任意类型的。第一种写法:name : “=”,这种写法html页面的自定义指令属性名就是name name : “=username”,属性名是username
& 使用父作用域中的一个函数,可以在指令中调用。第一种写法:getName:”&”,这种写法html页面的自定义指令属性名就是gegName getName : “&getUserName”,属性名是getUserName

其余两种符号用法:



 
自定义指定令的内容555


 

5.controller属性

controller属性用于提供对外的接口,即该自定义指令会被其他自定义指令调用。所谓的接口,就是this后的变量或方法。

controller可以使用的参数,作用域、节点、节点的属性、节点内容的迁移,这些都可以通过依赖注入被传进来,所以你可以根据需要只写要用的参数,有$scope,Z$element, $attrs, $transclude。

调用该自定义指令的指令需要放在该指令之间。假定firstDirective指令是要被调用的自定义指令,expander是调用者指令。如下:


  {{item.title}}:{{item.text}}
 

既然firstDirective内部还有指令,则firstDirective必须配置transclude属性为true。代码如下:

//用于被调用的自定义指令
 app.directive('firstDirective',function(){
  return {
   template : '
', 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一样。我在网上找了个例子来说明一下区别,代码如下:


 
Hello

注意打印结果:

levelOne: compile => 
  
   
    Hello 
   
  
 levelTwo: compile => 
   
    Hello 
   
 levelThree: compile => 
    Hello 
 levelOne: pre link => 
  
   
    Hello 
   
  

levelTwo: pre link => 
   
    Hello 
   

levelThree: pre link => 
    Hello 

levelThree: post link => 
    Hello 
levelTwo: post link => 
   
    Hello 
   

levelOne: post link => 
  
   
    Hello 
   
  

分析打印结果:

运行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页面代码:

控制器代码:

"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: '
' + '
    ' + '
  • «
  • ' + '
  • ' + '{{ item }}' + '
  • ' + '
  • »
  • ' + '
' + '
' + '第页 ' + '每页' + '/共{{ conf.totalItems }}条' + '
' + '
暂无数据
' + '
', 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自定义指令详解(有分页插件代码))