详说Angular之指令(directive)

KatlynColle 8年前

来自: http://www.cnblogs.com/CreateMyself/p/5153772.html

前言

angular核心部分如下图几大块,最重要的莫过于指令这一部分,本文将重点讲解指令这一部分,后续笔者将通过陆续的学习来叙述其他如:factory、service等,若有叙述错误之处,欢迎各位指正以及批评。本文将通过一些实例来进行叙述。

话题

restrict以及replace

在sublimeText中安装angular插件之后,我们需要创建指令时此时将自动出现如下定义:所以我们将重点放在如下各个变量的定义。

.directive('', ['', function(){          // Runs during compile          return {              // name: '',              // priority: 1,              // terminal: true,              // scope: {}, // {} = isolate, true = child, false/undefined = no change              // controller: function($scope, $element, $attrs, $transclude) {},              // require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements              // restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment              // template: '',              // templateUrl: '',              // replace: true,              // transclude: true,              // compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),              link: function($scope, iElm, iAttrs, controller) {                                }          };      }]);

首先我们只需要知道一个属性【restrict】,意思是替换的是什么,【E】:元素,【A】:属性,【C】:类名,【M】:注释。template(模板)自然就不用说了。下面我们来看一个有关指令最简单的例子。

【1】脚本:

    var app = angular.module('app', []);      app.directive("hello",function(){          return{              restrict:"EACM",              template:"<h1>Hello</h1>"          }      });

【2】html:

    <hello></hello>       <div hello></div>      <p class="hello"></p>      <!-- directive:hello -->

此时的结果是四个带h1标签的Hello吗?显然不是,如下:

不是说好的将restrict的模式设置为【EACM】,理论上应该是显示四个,结果却不是。即使你设置了四个也只会显示三个,而注释不会显示,此时要设置另外一个属性即【replace:true】才会显示四个注释。同时也需要注意,在注释中的hello和后面的--之间要有间隔,你可以试试。

transclude

当我们替换的元素里面可能还嵌套者其他元素,而其他元素里面有内容,我们不希望被覆盖,此时就需要将transclude设置为true并将要应用的元素标记为ng-transclude。如下:

【脚本】:

    var app = angular.module('app', []);      app.directive("hello",function(){          return{              restrict:"EACM",              transclude:true,              template:"<h1>Hello<div ng-transclude></div></h1>",          }      });

【html】:

    <hello>          <div>博客园,你好</div>      </hello>

结果如下:

tepmlateUrl

在实际开发中用template形式来给出模板似乎不太友好,一旦替换的内容比较多那么显得代码比较凌乱,此时我们就要用到templateUrl,将模板单独写在一个页面即可。这个就不用说了,在这个内容不得不说的是模板的缓存。

【脚本】:

    var app = angular.module('app', []);      app.run(function($templateCache){          $templateCache.put("hello.html","<h1>hello cnblogs</h1>")      });      app.directive("hello",function($templateCache){          return{              restrict:"AE",              template:$templateCache.get("hello.html"),              replace:true          };      });

【html】:

<hello></hello>

结果如下:

scope

在接触这个属性之前我们首先来看看一个例子。

【脚本】:

    var app = angular.module('app', []);      app.directive("hello",function(){          return{              restrict:"AE",              template:'<div><input type="text" ng-model="test"/>{{test}}</div>',              replace:true          };      });

【html】:

    <hello></hello><br/>      <hello></hello><br/>      <hello></hello>

我们来瞧瞧结果:

我们将鼠标放在第一个文本框输入xpy0928,下面两个同样也发生相应的改变,我们再试试将鼠标聚焦于第二个看看其他两个的改变如何:

由上知,同样如此,貌似三个文本框的作用域是一样的,一变都变,相互影响。在指令中,当我们需要保持各自的作用域时,此时就需要【scope】属性,并设置为true。我们再来试试

require

【指令与页面上已定义控制器进行交互】

在开始这个话题之前我们先看看指令与已定义的控制器如何进行交互?【注意是在页面上已定义的控制器】

【脚本】:

    var app = angular.module('app', []);      app.controller("ctrl",["$scope",function($scope){          $scope.win = function(){              alert("你赢了");          }      }])      app.directive("lay",function(){          return{              restrict:"AE",              scope:true,              template:'<div>点击我,有惊喜哦</div>',              replace:true,              link:function(scope,elment,attr){                  elment.on("click",function(){                      scope.win();                  })              }          };      });

【html】:

    <div ng-controller="ctrl" style="background-color:orange">          <lay></lay>      </div>

对于页面中已定义的控制器,指令为与其进行交互直接通过link上的scope来获取该控制器上的APi即可。

【指令与指令上控制器进行交互】

此时就需要用到require,此属性的作用是指令与指令之间的交互,说的更加具体一点就是与其他指令中控制器之间的交互。在指令中获取其他指令的控制器要用到link函数的第四个参数,link函数的前三个参数还是非常容易理解,不再叙述。那么是怎样在当前指令去获取该控制器呢?这时就需要用到require属性。

(1)require的值用?、^、或者?^修饰。

(2)如果不用任何修饰,则在当前指令中进行查找控制器。

(3)如果用^修饰,则在当前指令的父指令进行查找控制器,若未找到,则会抛出异常。

(4)如果用?修饰,则说明在当前指令中未找到控制器,此时将以null作为第四个参数。

(5)如果需要交互多个指令,则以数组形式给出,如:[^?a,^?b]。

鉴于此,为了不抛出异常,我们一般以^?来进行修饰。

就上面解释,我们来看一个例子,如下:

【脚本】:

    var app = angular.module('app', []);      app.directive('customdirective', function(){          return {                  controller: function($scope, $element, $attrs, $transclude) {                  var self = this;                  $scope.count = 0;                  self.add = function(){                      $scope.$apply(function(){                          $scope.count++;                      })                  }              },                      restrict: 'E',          };      }).directive('childirective', function(){          return {              require: '^customdirective',               restrict: 'E',              template: '<button id="add">点击增加1</button>',              replace: true,              link: function($scope, iElm, iAttrs, controller) {                          angular.element(document.getElementById("add")).on("click",controller.add);                              }          };      })

【html】:

<customdirective>      <div>次数:{{count}} </div>       <br/>      <childirective></childirective>  </customdirective>

对于指令上的link与指令上的controller的何时使用,我们可以这样概括:当一个指令上想向外部暴露这个指令时,此时利用controller进行暴露,而其他指令需要利用指令时,通过属性require和link上的第四个参数进行获取暴露指令的APi,否则的话我们不需要第四个参数。

那么问题来了,指令的作用到底是什么呢?

假如我们有几个控制器都需要用到相同指令但是对应不同事件时,此时难道需要定义不同的指令吗?答案肯定很显然不是,指令说到底就是为了【复用】。下面我们继续来看一个例子。

【脚本】:

    var app = angular.module('app', []);        app.controller("first",["$scope",function($scope){          $scope.first = function(){              alert("第一个控制器函数");          }      }])        app.controller("second",["$scope",function($scope){          $scope.second = function(){              alert("第二个控制器函数");          }      }])        app.directive('lay', function(){          return{                restrict:"AE",                link:function(scope,element,attr){                    element.on("click",function(){                        scope.$apply(attr.loader);                    })                }          };      });

【html】:

<div ng-controller="first">      <lay loader="first()">第一个控制器</lay>  </div>  <br/>  <div ng-controller="second">      <lay loader="second()">第二个控制器</lay>  </div>

当需要复用指令时,可以通过获取指令上属性对应的方法,最终利用apply方法应用到对应的控制器中。

结语

在这里我们稍微详细的叙述了指令中有关属性的用法,对于一些原理性的东西,毕竟不是做前端的,所以没有做过多的探讨。下面我们最后以一个实例来结束本文。

通过指令来使未验证通过的文本框聚焦。

【脚本】:

var app = angular.module('app', []);      app.controller('ctrl', function ($scope, $location, $rootScope) {        })       app.directive("parentDirective", function () {        return {            restrict: 'A',            require: ['form'],            controller: function () {                // nothing here            },            link: function (scope, ele, attrs, controllers) {                var formCtrl = controllers[0];            }        };        }).directive('input', function () {        return {            restrict: 'E',            priority: -1000,            require: ['^?parentDirective', '^?angularValidator'],            link: function (scope, elm, attr, ctrl) {                if (!ctrl) {                    return;                }                    elm.on('focus', function () {                    elm.addClass('apply-focus');                        scope.$apply(function () {                        ctrl.hasFocus = true;                    });                });                    elm.on('blur', function () {                    elm.removeClass('apply-focus');                    elm.addClass('apply-visited');                        scope.$apply(function () {                        ctrl.hasFocus = true;                        ctrl.hasVisited = true;                        });                });                }        };    });  

【html】:

 <form  ng-controller="ctrl" novalidate angular-validator >            <div class="gloabl-form-content">                                <div class="row">                    <div class="col yellow-divider">                        <div class="col-first">                            <label><span  style="color:red;">*</span>姓名</label>                        </div>                        <div class="col-second">                            <input name="name"                                type="text"                                ng-model="profile.name"                                validate-on="blur"                                ng-pattern="/^[ a-zA-Z]*$/"                                required                                required-message="'请输入姓名.'"                                ng-message="'不能输入数字'"                                maxlength="100" />                        </div>                          <div class="col-third">                            <label>住址</label>                        </div>                        <div class="col-four">                            <input name="addr" type="text" ng-model="profile.addr"                                validate-on="blur"                                ng-pattern="/^[ a-zA-Z]*$/"                                invalid-message="'不能输入数字'"                                maxlength="100" />                        </div>                    </div>                </div>                <div class="row">                    <div class="col yellow-divider">                        <div class="col-third">                            <label><span  style="color: red;">*</span>手机号码</label>                        </div>                        <div class="col-four">                            <input type="text"                                ng-model="customer.number"                                validate-on="blur"                                ng-pattern="/^[ 0-9.]*$/"                                required                                required-message="'请输入手机号码'"                                invalid-message="'只能输入数字'" />                        </div>                    </div>                </div>                </div>        </form>  

效果(1):

效果(2):

【擦,不行,快冻成傻逼了,手冻僵了,不能写了,就到这里诺。。。。。。。。。。】

</div>