使用Angularjs开发,很重要的一步是须要开发自定义的指令(custom directives)。
接下来分几个步骤记录如何开发一个自定义的指令。javascript
指令是什么?html
写一个基本的指令java
指令的属性说明node
Angularjs中的指令,我我的的简单的理解:指令=控件
指令实际是对Html元素的扩展,传统的服务端控件,例如asp.net中的服务器端控件(处理在服务端,最终编译成html返回给客户端),其实也是对Html元素的扩展。angularjs中的指令,最终其实也是通过angularjs(其实就是javascript)的编译处理,最终输出成html。具体的细节不继续细说,若是对angularjs毫无认识的童鞋,建议了解一些基本概念之后再看。angularjs
var simpleapp=angular.module('simpleapp'); simpleapp.directive('simpleDirective', function () { var directiveObj = { template:'<p>这是一个自定义指令</p>' } return directiveObj; });
<body ng-app="simpleapp"> <div ng-controller="app"> <div simple-directive></div> </div> </body>
以上就是一个简单的自定义指令,最终页面会输出指令里包含的内容。这里先不说属性,只先讨论应该在何处定义,下一节再说如何定义指令内部。浏览器
看上面示例,能够知道指令必须是在module下的,先得获取到模块对象simpleapp,而后使用directive函数来定义一个指令。安全
在上节例子中能够看到,simpleapp.directive第二个参数最终执行后须要返回一个对象(我叫它指令对象),这个指令对象里面有能够包含不少属性,大概有12个,这节主要解释这些属性的用法。
我主要是按功能来给这些属性分类并讲解。服务器
template和templateUrl都是用来指定指令模板的,模板是用来编译内容的一个基本模型。app
字符串或函数,能够指定为一个html字符串,也能够是一个特定的函数。
例子:asp.net
template: '<div></div>' //or template:function(tElement, tAttrs) { return "..." },
当为一个函数时,函数应该返回一个字符串。函数的tElement, tAttrs分别表明一个元素和元素对应的属性集合。
字符串或函数,能够指定一个模板的地址,也能够是一个特定的函数。
templateUrl: 'sometemplate.html' //or templateUrl:function(tElement, tAttrs) { return tAttrs.xx+"xx.html" },
当为一个函数时,函数应该返回一个字符串。函数的tElement, tAttrs分别表明一个元素和元素对应的属性集合。
字符串,这个属性用来限制指令匹配模式,能够有四种模式,分别是A(Attribute)、E(Element)、C(Class)、M(注释),这四种模式能够自由组合。
<!--属性模式,默认匹配--> <div simple-directive></div> <!--元素模式,默认匹配--> <simple-directive></simple-directive> <!--样式模式--> <div class="simple-directive"></div> <!--注释模式,主要两侧须要空格--> <!-- directive:simple-directive --> <div class="simple-directive"></div>
须要注意的是,若是不设置此属性,则默认匹配EA两种模式。
疑问:M的匹配方式是靠C控制的,即若是设置restrict:'C',实际匹配的是C和M,若是单独设置restrict:'M'则没法生效。--这个问题不知道是为何?
主要是标记文档类型,可设置为html、svg、math。具体使用场景我也不曾使用过。
此处贴上官方说明:
* String representing the document type used by the markup in the template. * AngularJS needs this information as those elements need to be created and cloned * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`. * * * `html` - All root nodes in the template are HTML. Root nodes may also be * top-level elements such as `<svg>` or `<math>`. * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`). * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`). * * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
优先级,当一个DOM上有多个指令时,有会须要指定指令执行的顺序。 这个优先级就是用来在执行指令的compile函数前先排序的。高优先级的先执行。 相同优先级的指令顺序没有被指定谁先执行
我的认为这个属性仍是比较难解释的,自己英文也不知怎么解释。我按我的理解叫作”局部替换“,这个属性一般是要和ng-transclude配合。
先说下官方的意思:
* Extract the contents of the element where the directive appears and make it available to the directive. * The contents are compiled and provided to the directive as a **transclusion function**. See the * {@link $compile#transclusion Transclusion} section below. * * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the * directive's element or the entire element: * * * `true` - transclude the content (i.e. the child nodes) of the directive's element. * * `'element'` - transclude the whole of the directive's element including any directives on this * element that defined at a lower priority than this directive. When used, the `template` * property is ignored.
我的理解是transclude开启时,在编译完成之后,会将编译后的内容放到ng-transclude指定的位置中,相似于占位符的做用。
我仍是写一个例子,这样可以更直观的看出它是什么意思
//JS simpleapp.directive('simpleTransclude', function () { return { template: '<div><div>这是模板原有的内容</div><div ng-transclude></div></div>', transclude: true }; }) //HTML <simple-transclude>这是指令内容</simple-transclude> //浏览器最终展示效果 <simple-transclude> <div> <div>这是模板原有的内容</div> <div ng-transclude=""> <span class="ng-scope">这是指令内容</span> </div> </div> </simple-transclude>
做用域,若是声明了,则指令会产生一个私有的做用域,若是有多个,则只有一个新的会被建立,也就是最后一个。对更节点无效。并且这个做用域是孤立的,不会以原型继承的方式直接继承自父做用域。这是须要注意的地方。关于scope的内容须要将的太多,放在之后的文章中详细说(难道说其实我本身也搞不清?)。
控制器的构造对象。这个控制器函数是在预编译阶段被执行的,而且它是共享的,其余指令能够经过它的名字获得(参考依赖属性[require attribute])。
注意它是在预编译的时候执行,这一点很是重要,这意味着你能够在预编译前,在这个controller中作一些你想作的事情。
AngularJS 1.2版本中提供了Controller As语法,简单说就是能够在Controller中使用this来替代$scope。其余与controller是同样的。
请求将另外一个控制器做为参数传入到当前连接函数。 这个请求须要传递被请求指令的控制器的名字。若是没有找到,就会触发一个错误。请求的名字能够加上下面两个前缀:
? - 不要触发错误,这只是一个可选的请求。
^ - 没找到的话,在父元素的controller里面也查找有没有。
编译函数,在时执行,且指执行一次。
function compile(tElement, tAttrs, transclude) { ... }
编译函数是用来处理须要修改模板DOM的状况的。由于大部分指令都不须要修改模板,因此这个函数也不经常使用。须要用到的例子有ngTrepeat,这个是须要修改模板的,还有ngView这个是须要异步载入内容的。编译函数接受如下参数。
tElement - template element - 指令所在的元素。对这个元素及其子元素进行变形之类的操做是安全的。
tAttrs - template attributes - 这个元素上全部指令声明的属性,这些属性都是在编译函数里共享的, 参考章节“属性”。
transclude - 一个嵌入的连接函数function(scope, cloneLinkingFn)。
注意:若是模板被克隆了,那么模版实例和连接实例可能不是同一个对象。 因此在编译函数不要进行任何DOM变形以外的操做。 更重要的,DOM监听事件的注册应该在连接函数中作,而不是编译函数中。
编译函数能够返回一个对象或者函数。
返回函数 - 等效于在编译函数不存在时,使用配置对象的link属性注册的连接函数。
返回对象 - 返回一个经过pre或post属性注册了函数的对象- 使你能更具体的连接函数的执行点。参考下面pre-linking和post-liking函数的解释。
连接函数
function link(scope, iElement, iAttrs, controller) { ... }
连接函数负责注册DOM事件和更新DOM。它是在模板被克隆以后执行的。 它也是大部分指令逻辑代码编写的地方。
scope - 指令须要监听的做用域。
iElement - instance element - 指令所在的元素。只有在postLink函数中对元素的子元素进行操做才是安全的,由于那时它们才已经所有链接好。
iAttrs - instance attributes - 实例属性,一个标准化的、全部声明在当前元素上的属性列表,这些属性在全部连接函数间是共享的。参考“属性”。
controller - 控制器实例,若是至少有一个指令定义了控制器,那么这个控制器就会被传递。控制器也是指令间共享的,指令能够用它来相互通讯。
Pre-linking function 在子元素被连接前执行。不能用来进行DOM的变形,觉得可能致使连接函数找不到正确的元素来连接。
Post-linking function 全部元素都被连接后执行。能够操做DOM的变形。
link和compile是指令的关键部分,留在下一章中详细讨论。