11 指令详解

指令详解

指令就是一些附加在HTML元素上的自定义标记(例如:属性,元素,或css类),它告诉AngularJS的HTML编译器 ($compile) 在元素上附加某些指定的行为,甚至操作DOM、改变DOM元素,以及它的各级子节点。

通过自定义指令访问DOM: 对于Angular,一个程序中唯一允许接触DOM的地方就是“指令”。之所以这样要求,是因为需要访问DOM的代码难以进行自动化测试。 如果你需要直接访问DOM,那么你就应该为此写一个自定义指令。

指令的生命周期开始于 $compile 方法并结束于 link 方法。

指令的匹配

Angular把一个元素的标签和属性名字进行规范化,来决定哪个元素匹配哪个指令。 我们通常用区分大小写的规范化命名方式–驼峰法–(比如ngModel)来识别指令。 然而,HTML是区分大小的,所以我们在DOM中使用的指令只能用小写的方式命名, 通常使用破折号间隔的形式(比如:ng-model).

即,使用驼峰法来命名一个指令, runoobDirective, 但在使用它时需要以 - 分割, runoob-directive

$compile(编译) 可以基于元素名字、属性、类名和注释来匹配指令的。Angular提供的所有指令都支持元素名、属性、类名和注释的形式。

Module.directive详解

语法:Module.directive(name,directiveFactory);

调用这个方法表示这个模块将在$compileProvider中注册一个指令

参数:

  • name:指令名称。
  • directiveFactory:指令构造函数。

module.directive 接受规范化normalized 的指令名字和工厂方法。此工厂方法应该返回一个带有不同选项的对象来告诉 编译器$compile此指令被匹配上该做些什么。

工厂函数仅在 编译器 第一次匹配到指令的时候调用一次. 你可以在这里进行初始化的工作。 该函数使用$injector.invoke调用,所以它可以像控制器一样进行依赖注入。

module.directive用return语句返回另一个指令函数,使用该自定义指令,Angular就会调用这个函数。

指令函数接受三个参数,第一个scope,用于检查在视图中可用的数据。 第二个参数element是一个jqLite对象,jqLite是Angular提供一个剪裁版本的jQuery。第三个参数是attrs。

指令定义的对象为编译器提供说明,其属性有:

  • priority:Number

    当有一个DOM元素中定义了多个指令,有时有必要指定指令的应用顺序。在编译函数被调用之前,先将其按优先级排序。这个属性的值越大,优先级越高,将会更先被编译。具有相同优先级的指令的顺序是未定义的。默认优先级为0。

  • terminal: Boolean

    如果该属性设置为true,那么当前指令的优先级会放在最后一组被执行(在当前优先级的任何指令将仍然作为未定义优先级的设置执行)

  • scope:Boolean or Object

    • 如果设置为false,则不会为指令创建任何scope。指令使用其父级scope。
    • 如果设置为true,那么该指令将会创建一个新的子scope,这个scope继承父级的属性。如果在一个DOM的多个指令上定义新作用域,也只能创建一个新的作用域。模板有了新的作用域后,该作用域的规则不适用与模板所在的根节点。
    • 如果设置为{},那么将会创建一个新的、隔离的作用域。该作用域不继承其父作用域,这样就不会在创建可重用的指令时影响到父作用域的数据。
      这个隔离的作用域会有一组来自父作用域的数据组成的对象,该对象名称前缀为:
      • @ or @attr : 结合局部作用域属性的DOM元素上的属性值。在DOM元素上和指令scope中以string类型绑定。如果没指定名称,那么与假定的指令作用域中的名称相同。
      • = or =attr:在指令作用域和父作用域之间设置数据的双向绑定。如果没指定名称,那么与假定的指令作用域中的名称相同。
      • & or &attr:提供了在父作用域的上下文中执行的表达式。
  • controller:String or function(scope, element, attrs, transclude, otherInjectables) { ... }

    该指令的控制器构造函数。控制器将会在预编译之前被实例化,并且可与其他指令共享(参见require属性)。这实现了指令之间的通信,增强了相互之间的行为。可写入以下参数:

    • $scope:与元素相关联的当前作用域。
    • $element:当前元素。
    • $attr:当前元素上的属性。
    • $transclude:一个被预绑定到正确的嵌入范围的linking函数。该范围可以通过一个第一个可选的参数重写。function([scope], cloneLinkingFn).
  • require:String

    需要另外一个指令并将其控制器作为第四个参数注入linking函数。需要以指令的字符串名称(或字符串数组)注入。如果使用了数组,注入的参数将是一个数组中的相应的顺序。如果没有找到这样的指令,或如果该指令没有控制器,那么将会出现一个错误。该属性名称前缀为:

    • 无前缀:定位当前元素所需的控制器。如果未找到错误,抛出一个错误。
    • ?:试图找到所需的控制器,如果没有找到,则通过空函数。
    • ^:通过搜索元素的父节点来寻找所需的控制器,如果没找到,抛出个错误。
  • controllerAs:String

    指令作用域内的控制器的别名。控制器的别名,以便在该指令模板中引用。该指令需要在适用范围内定义此配置。当指令被用作组件的情况下有用。

  • restrict:String

    EACM的子集,限制了对一个特定的指令声明式的指令。

    • E:元素名称,<my-directive></my-directive>
    • A:元素属性,<div my-directive="exp"></div>
    • C:元素类:<div class="my-directive: exp;"></div>
    • M:注释,<!-- directive: my-directive exp -->
  • template:String or Template Function

    将HTML的内容的指令块替换当前元素。过程将在新元素上更新之前旧元素的所有属性/类。该属性可以指定模板作为一个字符串或一个函数模板,采用两个参数tElement和tAttrs,并返回一个表示模板的字符串值。

  • templateUrl:String

    与template基本相同,但模板通过指定的url加载。因为模板是异步加载的,所以complie和link都会被占用,等待模板加载完。

  • replace: Boolean or String

指定要插入模块的位置,默认为false。

* true:模板将会替换当前元素。
* false:模板将会替换当前元素的内容。
  • transclude:Boolean

    编译元素的内容并且使其在指令内有效。这使得组件有私人的状态,并且嵌入的部分包含到父作用域。

    • true:该指令可嵌入内容。
    • false:嵌入整个单元包括在较低的优先级定义的任何指令。
  • compile

    function(tElement, tAttrs, transclude) {
    return {
    pre: function(scope, iElement, iAttrs, controller) { ... },
    post: function(scope, iElement, iAttrs, controller) { ... }
    }
    // 或者
    // return function postLink(...) { ... }
    }

    compile函数分配模板的转换。由于大多数指令不做模板转换,所以它不经常使用。需要用到compile去转换模板的例子有ngRepeat,或者异步加载内容,如ngView。编译需要如下参数:

    • tElement:模板元素,该指令已声明的元素。只有在元素和子元素上进行模板转换是安全的。
    • tAttr:模板属性,在所有的指令compile函数之间共享的属性。
    • transclude:一个transclude linking函数– function(scope, cloneLinkingFn)
      备注:如果模板被克隆,那么template实例和link实例是不同的对象。因此,所有在compile函数里克隆的DOM节点做DOM转换都是不安全的。具体来说,DOM监听应该在link函数里而不是在compile函数内。
      注意:compile函数不能操作指令去递归使用自身的模板或compile函数。编译这些指令将会导致一个无限循环和堆栈溢出错误。可以通过手动使用postLink函数强制编译指令的模板而不是依靠通过template或templateUrl或在compile函数模板手动编译。
  • link

    如果未定义compile属性,则使用此属性。

    function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

    link函数负责注册DOM监听及更新DOM操作,该函数在模板被克隆后执行。大部分指令逻辑是放在这里面的。

    函数参数:

    * scope:被注册监听的指令使用的作用域。
    * iElement:元素实例,使用该指令的元素。当子元素已被关联,那么只能在postLink函数内操作是安全的。
    * iAttrs:属性实例,在这个元素上声明的属性,在所有指令的linking函数内共享。
    * controller:如果至少有一个指令的元素定义一个控制器,那么是个控制器实例。控制器在所有的指令中被共享,它允许指令使用控制器作为通信通道。
    * transcludeFn:预绑定到正确的嵌入范围的一个linking函数。范围可以通过一个可选的第一个参数重写--function([scope], cloneLinkingFn)
    
  • pre-linking 函数:在关联子元素之前执行。

  • Post-linking函数:在关联子元素之后执行。

指令隔离作用域绑定策略

为了让新的指令作用域可以访问当前本地作用域中的变量,需要使用下面三种别名中的一种。

  • 本地作用域属性:使用@符号(@ (or @attr))将本地作用域同DOM属性的值进行绑定。指令内部作用域可以使用外部作用域的变量。
  • 双向绑定:通过=(= (or =attr))可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。就像普通的数据绑定一样,本地属性会反映出父数据模型中所发生的改变。

  • 父级作用域绑定:通过&符号(& (or &attr))可以对父级作用域进行绑定,以便在其中运行函数。意味着对这个值进行设置时会生成一个指向父级作用域的包装函数。要调用带有一个参数的父方法,我们需要传递一个对象,这个对象的键是参数的名称,值是要传递给参数的内容。

例如,假设我们在开发一个电子邮件客户端,并且要创建一个电子邮件的文本输入框:

<input type="text" ng-model="to"/>
<!-- 调用指令 -->
<div scope-example ng-model="to"
on-send="sendMail(email)"
from-name="[email protected]" />
scope: {
ngModel: '=', // 将ngModel同指定对象绑定
onSend: '&', // 将引用传递给这个方法
fromName: '@' // 储存与fromName相关联的字符串
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="../../css/bootstrap.css" rel="stylesheet">
<script src="../../script/angular.min.js"></script>
<script>
var m= angular.module("my", [])
m.controller("ctr1", function ($scope) {
$scope.dayNames = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
$scope.today = $scope.dayNames[new Date().getDay()];
//getDay星期是从0开始的,星期日的下标是0
});
m.controller("ctr2", function ($scope) {
$scope.dayNames = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
$scope.tommorrow = $scope.dayNames[(new Date().getDay() + 1) % 7];
});
m.directive('highlight', function () {
return function (scope,element,attrs) {
if(scope.today == attrs['highlight']){
element.css("color",'red');
}else{
element.css("color",'green')
}
}
})
</script>
</head>
<body ng-app="my">
<div class="container">
<h2>今天是星期几?</h2>
<p ng-controller="ctr1" highlight="星期二">今天是{{ today }}</p>
<p ng-controller="ctr2" highlight="星期一">明天是{{ tommorrow }}</p>
</div>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/fighting_no1/article/details/80036230