参考文章 :

http://www.zouyesheng.com/angular.html#toc20    18. 自定义指令directive

http://blog.jobbole.com/62249/

http://blog.jobbole.com/62999/

https://docs.angularjs.org/api/ng/service/$compile

https://docs.angularjs.org/guide/directive

http://www.cnblogs.com/lvdabao/p/3398044.html

http://loveky.github.io/2014/04/01/angularjs-directivecompilelink/

directive 是angular 用于扩展html标签的方案.

它使得我们可以在模板上写入自定义的元素或标签,通过angular的扫描执行一些改装事件绑定等等。

angular 也提供了很多的指令比如 ng-click,ng-repeat 等等

首先大致说一下整个执行过程.

1.angular 调用$compile来偏离我们的html文档, 把dom和指令关联在一起打包起来

2.对每个element的指令排序

3.检查指令如果有template就replace等等,templateUrl 会发出异步请求(回来的时候应该其它dom已经做完了)

4.循环调用指令中的 compile,所以我们在compile可以调用$element修改模板 (这个时候模板还没有被插入到document中)

5.把每个compile返回的link函数收集起来

6.link函数可以分为 pre 和 post (pre 和 post 最要区别在执行持续, pre 是从parent to child , post 则是 child to parent) 参考http://jsfiddle.net/loveky/K93SE/light/

7.循环调用 pre link , 并把scope传入, 这时我们的参数$attrs 中的值是还没解析的表达式 ,

8.循环执行 post link , 把scope传入,这时的$attrs 已经放入了$scope的值, 我们可以做一些绑定监听等

9.调用$digest渲染.

最终就是美美的DOM啦 .

小总结 : $compile -> 运行全部节点指令的compile , 一个一个节点运行 pre link, 一直到子层完 , 调用post link 一直到parent , 去下一个兄弟节点,再次运行pre link... (ng-controller 是最优先的pre link)

我们现在一步一步来看具体实现

        angular.module("app", [], angular.noop).
directive("myelem1", function () {
return {
restrict : "E",
link: function (scope, element, attrs, controllers) { }
}
});

这个是创建方法

retrun 一个对象,里面包含了指令的config等等 .

具体属性有 :

  • name
  • priority
  • terminal
  • scope
  • restrict
  • template
  • templateUrl
  • replace
  • transclude
  • compile
  • link
  • controller
  • require

我们逐一介绍

restrict 

用于表示我们的指令类型,支持4中pattern , 很好记叫ECMA

E = elemet   <myelem></myelem> 元素

C = class     <div class="myelem"> class 中的属性,基本不用

M = 注释      <!-- directive: my-dir exp --> 基本不用

A = attr       <div myelem="??">  标签

restrict = "ECMA"  可以同时放多个

template

template : "<div>123</div>"; 写模板,这里的模板和一般写的版本相同,没有任何限制

默认情况下,模板将被append进当前的指令元素

templateUrl

也可以使用模板地址代替string模板, 如果一个指令使用template的话,它会被异步编程,请求template时,angular会先解析其它element指令先

replace

replace : true 表示把模板直接替换掉当前的指令元素。这里有个限定,元素必须是一对一的对换,简单说就是模板必须被一层div包到完只能有子层不能有兄弟。

priority

priority : 100  放一个number , 在一个元素上设置指令的解析次序。如果2个指令相同,那么angular不会区分那个先执行。(越小的代表优先,0是default)

需要特别注意,当我们在一个element上放置了多个指令,会有一下的限制

  • Multiple directives requesting isolated scope. //要隔离的scope
  • Multiple directives publishing a controller under the same name. 不懂
  • Multiple directives declared with the transclusion option. //不懂
  • Multiple directives attempting to define a template or templateURL. //只能有一个指令用于带有模板

scope

scope : false | true | {@attr: '引用节点属性', =attr: '把节点属性值引用成scope属性值', &attr: '把节点属性值包装成函数'}

false 表示直接使用指令位于的controller的$scope

true 表示new一个$scope继承controller $scope

{..} 表示new一个$scope不继承,但是可以通过 @=& 来和元素上的标签做桥梁,在标签上就可以和controller $scope的属性做一些连接了

这里特别说一下,如果是用继承,那么指令本身的scope是新的,如果用隔离scope,指令本身的scope不是新的,而指令模板的scope才是新的 (大家可以自己玩玩看^^)

那么来看看 @=&的区别和用法。

场景是一个 ctrl 和 一个 dir

如果我们想做一个单向绑定, 即 ctrl 传一个值给 dir, 之后只有 ctrl一方的值能同步到 dir. 我们使用 "@" (即使值是个object,它依然是单向绑定,angular传入的不是引用)

如果我们想做双向绑定,即 dir 的值也能同步到 ctrl . 我们使用 "="

如果我们想传的是方法,我们使用 "&"

注意 : “&” 也是单向绑定的。如果是要穿个动态方法,我们可以使用 "=" 把方法当值来传也是可以的。

     另外对于依赖跟踪属性,我们也可以把它们当方法来看待,使用 "&"

新手 : 不要以为能用"@"的地方就能用"=",这是不对的概念,会遇到很多坑的。

下面我们指出了一些小区别,要留意

<mydir single-name="{{singleName}}" //@ 要使用 {{ 属性名 }} , 如果没有{{}}表示它是个值,之后不会有任何同步绑定。
double-name="doubleName" //= 只写属性名就好
click="click()"> "&" 要使用 属性名()
</mydir>
scope: {
singleName: "@",
doubleName: "=",
click: "&"
},
//在link函数中
log(attr.singleName); //使用 @ 这里是"keatkaet1" <-获取到值 , 使用 = 这里是"doubleName" <-只是属性名 , 使用 & 这里是 "click()" <- 属性名()

terminal

terminal : true 表示在一个元素上priority大于这个指令的指令将不会被处理了。

transclude

使用 ng-transclude 要特别注意, 它会自动帮我们创建一个scope,并继承"跨parent scope" .

在做坎套指令时, 比如 ctrlA内有b指令内有c指令 , 在b模板中我们ng-transclude了c , 那么c的$scope会自动被创建新的并继承A, 而不是B哦!B-C是sibling $scope 的关系 .

这样的好处是如果指令B是一个隔离scope,那么我们的C指令还是有办法可以访问到A$scope内容。

这种情况只有ng-transclude可以做到,那么如果我们不希望这样,而是要C继承B的话,那么我们可以自己写一个my-transclude.在link中调用transludeFn来实现即可

transclude : true | "element"

先说true的情况

 <myelem2>
   <div>123</div> <%--这一段,指令元素的子层会被cut进去模板中--%>
</myelem2> directive("myelem2", function () {
return {
restrict: "E",
link: function (scope, element, attrs, controllers) {
alert("myelem2");
},
transclude: true,
template: '<div ng-transclude></div>', //模板中其中一个元素放入指令 ng-transclude,外边的子层就会被cut进来这里了
replace : true
}
})

"element" 的意思是把整个指令元素都cut进来,所以如果你的设置是replace= false 的话,就不会有东西出现了。

                link: function (scope, iElement, iAttrs, controller, transcludeFn) { //如果是"element",可以调用transcludeFn
//可以在这里for loop 调用transcludeFn ,然它clone多多出来
transcludeFn(scope, function (clone) { //传入scope,和方法
clone.css('background-color', 'yellow'); //这里的clone是angular帮我们clone出来的阶段
iElement.after(clone);
})
alert("myelem2");
},

参考ng-repeat,它也是用link 而不是compile 来做循环clone. 所以也搞不清楚compile适合什么场景使用.

require 和 controller

这2个关系密切,就一起介绍吧。

很多时候我们会用多层指令来做一个插件。

比如 :

<myelem1>
  <myelem2>
    <myelem3></myelem3>
  </myelem2>
</myelem1>

那么它么之间要互相沟通的话可以使用 require 和 controller 来完成

        directive("myelem1", function () {
return {
restrict: "E",
link: function (scope, iElement, iAttrs, controller, transcludeFn) { },
template: '<div ng-transclude></div>',
replace: true,
transclude: true,
scope: true,
controller: function ($scope) {
this.name = function () { //对外提供方法
alert("name");
}
},
//name: "myelem1Ctrl" //如果没有取名,那么会自动用指令名来代替
}
}).

在一个指令写上controller 和一个 name (name 不写的话会自动用指令的名字替代)

那么这个指令就可以被它子层和同层(同层包括next prev elem指令)的指令require了

这里的controller 是用依赖注入的,和 link complie 不同哦

一般我们会把这个controller 也当成service 或者一个对外开放的接口来看待。

其它指令通过 controller.someFn 来操作我们的scope

        directive("myelem3", function () {
return {
restrict: "E",
link: function (scope, iElement, iAttrs, controllers, transcludeFn) {
controllers[0].name();
controllers[1].name();
},
template: '<div>myelem3</div>',
replace: true,
require: ["?^myelem1","?^myelem2"], //它可以找多层,但是找不到普通的controller,只能找指令的controller
}
})

require 可以是一个array , ?代表如果找不到将返回undefined, ^是像上层(多层)寻找controller,默认是只在同层element上寻找其它指令

require 只能访问指令的controller,在 link 函数中我们就是用到 controllers了

还有个另类例子 :

                    controller("ctrl", ["$scope", function ($scope) {
$scope.name = "keatkeat";
this.xyz = "ggc";
}]).
directive("dir1", [function () {
return {
restrict: "E",
controller: "ctrl",
name: "ctrl123",
link: function (scope) {
console.log(scope.name);
}
}
}]);

指令的ctrl写入string , 它会去寻找 controller,通过name也可以重命名 , 不过一般上我们不这样做,容易混淆。

note :  指令写controller不会自动加scope,只有写scope:true|{} 才会.

compile 和 link

我对compile 和 link 只是一知半解

angular 会先执行 compile 等全部指令编辑完了才执行link ,执行compile的时候只操作DOM 不涉及 scope , compile 函数是访问不到scope的。

function compile(tElement, tAttrs, transclude) { ... }

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

这2个函数的参数都不是依赖注入的.

transclude 是一个方法,可以实现clone上面介绍过了

一般上 compile 必须返回一个link 函数 或者一个带有 pre , post 方法的对象

当一个节点有多个指令时 angular 会完成所有的 prelink 之后才执行 post link . 这允许我们在最后关头在多个指令间执行一些操作 pre link 是可以调用scope 的.

小总结 :

指令我觉得有2个重点。

一是template的结构,重要的是要理解 $compile 的过程还有 transclude的用法 ,ng-repeat 是比较复杂的例子,可以参考.

二是指令间的通讯 ,要理解 scope,require,controller. 在做表单控件时经常会和ngModel controller || ngform controller 打交道.

外传 :

当我们使用transclude = true 时,在我们的 compile : function(elem,attrs,link) 第3个参数是这个指令的 $compile(elem.innerHTML), 返回的link函数.

directive 指令的更多相关文章

  1. 学习AngularJs:Directive指令用法

    跟我学AngularJs:Directive指令用法解读(上) http://blog.csdn.net/evankaka/article/details/51232895 跟我学AngularJs: ...

  2. 学习AngularJs:Directive指令用法(完整版)

    这篇文章主要学习AngularJs:Directive指令用法,内容很全面,感兴趣的小伙伴们可以参考一下   本教程使用AngularJs版本:1.5.3 AngularJs GitHub: http ...

  3. angular上传获取图片的directive指令

    在AngularJS中,操作DOM一般在指令中完成,那么指令是如何实现的呢?指令的作用是把我们自定义的语义化标签替换成浏览器能够认识的HTML标签 一般的事件监听是在对静态的dom绑定事件,而如果在指 ...

  4. AngularJs:Directive指令用法

    摘自:http://www.jb51.net/article/83051.htm 摘要:Directive(指令)是AngularJ非常强大而有有用的功能之一.它就相当于为我们写了公共的自定义DOM元 ...

  5. AngularJS中Directive指令系列 - 基本用法

    参考: https://docs.angularjs.org/api/ng/service/$compile http://www.zouyesheng.com/angular.html Direct ...

  6. 关于Angularjs写directive指令传递参数

    包子又来啦.... 在Angularjs当中,我们可能会经常要写directive指令.但是指令如果要共用的话,肯定是有细微的差别的,所以这些差别可能需要一个参数来决定 所以如何在指令中传递参数呢.. ...

  7. angularjs中directive指令与component组件有什么区别?

     壹 ❀ 引 我在前面花了两篇博客分别系统化介绍了angularjs中的directive指令与component组件,当然directive也能实现组件这点毋庸置疑.在了解完两者后,即便我们知道co ...

  8. AngularJS中Directive指令系列 - scope属性的使用

    文章是转的,我做下补充.原文地址:https://segmentfault.com/a/1190000002773689 每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部 ...

  9. AngularJS directive 指令相关记录

    .... .directive('scopeDemo',function(){ return{ template: "<div class='panel-body'>Name: ...

  10. angular源码分析 摘抄 王大鹏 博客 directive指令及系列

    链接地址:http://www.cnblogs.com/web2-developer/p/angular-14.html $compile的功能:将一个html字符串或者一个DOM进行编译,返回一个链 ...

随机推荐

  1. 安装与使用smarty

    1.安装 下载最新的smarty.下载地址:http://www.smarty.net/download 下载成功后,解压压缩包后的文件如图所示: 将解压后的文件存放在web文档根目录外的某个位置.w ...

  2. JavaScript……

    退役了好伤心…… 这几天搞研究性学习写网页版贪吃蛇代码……太蛋疼了 要学javascript,就还要搞AJAX.JQuery.JSON…… 我感觉整个人都不好了

  3. [LeetCode] 230. Kth Smallest Element in a BST 解题思路

    Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. Not ...

  4. POJ 3723 Conscription

    Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6325   Accepted: 2184 Desc ...

  5. Linux内核ROP学习

    0x00 前言 1.SMEP(Supervisor Mode Execution Protection):一种减缓内核利用的cpu策略,禁止内核态到用户态内存页的代码执行(32位的addresses ...

  6. ExifInterface 多媒体文件附加信息

    简介         ExifInterface类主要描述多媒体文件比如JPG格式图片的一些附加信息,包括拍摄时的光圈.快门.白平衡.ISO.焦距.日期时间等各种和拍摄条件以及相机品牌.型号.色彩编码 ...

  7. html 文字溢出标签

    overflow:visible;作用:能看到溢出部分. overflow: hidden;作用:溢出部分看不到 overflow:scroll; 作用:出现一个滚动条(不超过的文字也会在滚动条里) ...

  8. python自动化执行脚本

    ---恢复内容开始--- 1 (1)首先在你的.py文件上加上一行代码注释: #!/usr/local/bin/python2.7 (2)终端下执行: crontab -e 进入后,输入i 进入可编辑 ...

  9. NHIBERNATE之映射文件配置说明(转载4)

    二十.自定义值类型   开发者创建属于他们自己的值类型也是很容易的.比如说,你可能希望持久化Int64类型的属性, 持久化成为VARCHAR 字段.NHibernate没有内置这样一种类型.自定义类型 ...

  10. c# 交换两个变量

    使用临时变量: 有人会问只使用两个变量交换,怎么办? 不实用临时变量: 第一种: a=a+b; b=a-b; a=a-b; 第二种: 异或:相同是0,不同是1 上面是整型的,那么字符串可以直接异或吗? ...