这是一篇译文,来自angular开发者说明的指令。主要面向已经熟悉angular开发基础的开发者。这篇文档解释了什么情况下需要创建自己的指令,和如何去创建指令。
什么是指令
从一个高的层面来讲,指令是angular $compile服务的说明,当特定的标签(属性,元素名,或者注释) 出现在DOM中的时候,它让编译器附加指定的行为到DOM上。
这个过程是很简单的。angular内部有很用这样自带的指令,比如说ngBind, ngView,就像你创建控制器和服务一样,你也可以创建自己的指令。当angular启动的时候,Angular的编译器分析html去匹配指令,这允许指令注册行为或者改变DOM。
匹配指令
在写指令之前,我们首先需要知道的是angular是如何匹配到一个指令的,在以下的例子我们说input元素匹配到ngModel指令.
<input ng-model="foo">
下面这种方法同样也会匹配到ngModel:
<input data-ng:model="foo">
Angular会规范化一个元素的标签名和属性名,以便确定哪一个元素匹配到哪一个指令。我们在js中是通过使用规范化后的驼峰式的名字来引用指令(比如ngModel)。在HTML中常常使用'-'划定的属性名字来调用指令(比如说ng-model).
规范化的处理过程:
-去掉元素或属性上面的x-和data-的前缀
-转化':','-'和‘_-'形式的命名为驼峰式拼写
以下例子展示了用不同的方式匹配到ngBind指令
<span ng-bind="name"></span> <br/> <span ng:bind="name"></span> <br/> <span ng_bind="name"></span> <br/> <span data-ng-bind="name"></span> <br/> <span x-ng-bind="name"></span> <br/>
Best Practice: 优先使用'-'格式的命名(比如说ng-bind匹配ngBind)。如果你想在HTML验证工具中通过,你可以用'data-'前缀的方式(比如data-ng-bind)。其他格式的命名是因为历史遗留的原因存在,避免使用它们。
$compile服务可以基于元素的名字,属性名,类名,和注释来匹配指令
所有Angular内部提供的指令都匹配属性名,标签名,注释,或者类名。以下不同的方式都可以被解析到
<my-dir></my-dir> <span my-dir="exp"></span> <!-- directive: my-dir exp --> <span></span>
Best Practice: 优先利用标签名和属性名的方式使用指令。这样子更容易理解指定的元素匹配到了哪个元素。
Best Practice: 注释的方式通常被用在DOM API限制创建跨越多个元素的指令,比如说table元素,限制重复嵌套,这样就要用注释的方式。在AngularJS 1.2版本中,通过使用ng-repeat-start 和 ng-repeat-end 作为一个更好的方案来解决这个问题。在可能的情况下,推荐使用这种方式。
文本和属性的绑定
在编译过程中,编译器会使用$interpolate服务来检测匹配到的文本和属性值是否包含内嵌表达式。这些表达式被注册为watches,可以在digest循环时被更新。
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
ngAttr属性的绑定
浏览器有些时候会对它认为合法的属性值非常的挑剔(就是某些元素的属性是不可以任意赋值的,否则会报错)。
比如:
<svg> <circle cx="{{cx}}"></circle> </svg>
使用这样的写法时,我们会发现控制台中报错Error: Invalid value for attribute cx="{{cx}}". .这是由于SVG DOM API的限制,你不能简单的写为cx="{{cx}}".
使用ng-attr-cx 可以解决这个问题
如果一个绑定的属性使用ngAttr前缀(或者ng-attr), 那么在绑定的时候将会被应用到相应的未前缀化的属性,这种方式允许你绑定到需要马上被浏览器处理的属性上面(比如SVG元素的circle[cx]属性)。
所以,我们可以这样写来修复以上的问题:
<svg> <circle ng-attr-cx="{{cx}}"></circle> </svg>
创建指令
首先我们来谈论下注册指令的API,跟controller一样,指令是注册在module上,不同的是,指令是通过module.directive API来注册的。module.directive接受的是一个规范化的名字和工厂函数,这个工厂函数返回一个包含不同配置的对象,这个对象用来告诉$compile服务如何进行下一步处理。
工厂函数仅在编译器第一次匹配到指令的时候调用一次。通常在工厂函数中执行初始化的工作。该函数使用$injector.invoke调用,所以它可以像controller一样进行依赖注入。
Best Practice: 优先返回一个定义好的对象,而不是返回一个函数。
接下来,我们先会了解一些常见的例子,然后再深入了解不同的配置项的原理和编译过程。