欢迎大家讨论与指导 : )

  

 前言

  当AngularJS中的内置指令不能满足我们的需求,或者当我们需要创建一个能够用于多个AngularJS程序的自包含的功能单元时,我们应该创建自定义指令来满足需求。

一、jqLite

  jqLite是AngularJS中使用的一个"精简版JQuery"。我们可以使用jqLite来返回一个jqLite对象,从而通过这个jqLite对象使用jqLite的操作函数。在指令内部的element参数其本身也是一个jqLite对象。这里有一个特别要注意的地方:当我们使用JavaScript数组的索引将返回一个HTMLElement对象,而不是一个jqLite对象。这个HTMLElement对象是不支持jqLite的方法的!下面来个例子说明

//指令内部
return function(scope, element, attrs){
var items = element;//现在element和items都是jqLite对象
for(var i = ; i< items.length; i++){
if(items.eq(i).text() == "Oranges"){//使用jqLite的eq()方法获取一个jqLite对象
items.eq(i).css("font-weight", "bold");
}
}
}

  可以看到,当我们使用jqLite方法会返回一个jqLite对象,而这个对象能够使用jqLite的方法(此处为css())。现在用JavaScript索引来遍历

//指令内部
return function(scope, element, attrs){
var items = element;//现在element和items都是jqLite对象
for(var i = 0; i< items.length; i++){
if(items[i].text() == "Oranges"){//使用索引遍历只会获得一个HTMLElement对象
items[i].css("font-weight", "bold");
}
}
}

  耶~报错啦~重要的事情说三遍HTMLElement对象是不支持jqLite的方法的!HTMLElement对象是不支持jqLite的方法的!HTMLElement对象是不支持jqLite的方法的!

  建议:在指令内部的元素遍历最好是使用jqLite的eq()find()方法。

 二、创建自定义指令

    二 . 1 命名规则 

  我们要在创建指令时使用峰驼式命名,例如指令是 <div unordered-list></div>  在声明指令时我们需要这样子写 app.directive("unorderedList", function(){..})  同理,当我们想要获取包含指令的元素的属性时,例如 <div unordered-list="product"></div> ,也要使用峰驼式的命名规则 var attrProduct = attrs["unorderedList"]

    二 . 2 从作用域中获取数据

  指令的link函数包含三个参数,分别是scope, element, attrs 其中,scope参数代表该指令被应用到的视图所在控制器的作用域。简单来说就是,这个指令当前在哪个controller的作用域下,scope就相当于这个作用域$scope了。假设,我们的"unorderedList"指令在defaultController控制器的作用域下,那么我们如何通过从这个作用域中获取我们所需要的$scope.product呢(这里举个例子)

app.controller("defaultController", ['$scope', function($scope){
$scope.product = [
  {name: 'Apples', category: 'Fruit', price: 1.20, expiry: 10},
  {name: 'Bananas', category: 'Fruit', price: 2.42, expiry: 7},
  {name: 'Pears', category: 'Fruit', price: 2.02, expiry: 6}
]
}]); app.directive("unorderedList", function(){
return function(scope, element, attrs){
  var data = scope[attrs["unorderedLsit"]]
}
})

  答案是通过为包含指令的元素的属性设置具体的值(这里为product),例如 <div unordered-list="product"></div> ,然后通过attrs属性获取这个具体的值 var data = scope[attrs["unorderedLsit"]]

  二 . 3 打破对数据属性的依赖

  对于指令函数中,我们应当尽量减少对代码过程的"假设",并通过为包含指令的元素增加具体的属性来增强指令的复用性。例如我们"假设"我们在上面例子所获取的data var data = scope[attrs["unorderedLsit"]] 中的每个item中都含有name属性,我们会这样子操作我们的代码:

for(var i = 0; i < data.length; i++){
var str = data[i].name
// .... "假设"有name属性
}

  但其实我们应该这样子操作  <div unordered-list="product" list-property="name"></div>  我们通过加入另一个list-property属性并为其赋上具体的值"name",来让我们不需要进行"假设",从而提高指令的复用率

for(var i = 0; i < data.length; i++){
var property = attrs["listProperty"]
var str = data[i].property
// .... 不要进行"假设"
}

  二. 4 使用$eval进行计算

  当我们指令的属性需要和filter过滤器或者其他一些组件一起工作的时候<div unordered-list="product" list-property="price | currency"></div> 例如这里,我们需要指令获取price属性,并且需要配合filter currency来进行预处理,我们应该怎么样操作呢? 答案是$eval,通过获取表达式并通过$eval运行,即可达到我们所需要的效果。注:$eval的第一个参数是具体的表达式,第二个参数是传到表达式进行计算的参数。

for(var i = 0; i < data.length; i++){
var propertyExrpession = attrs["listProperty"]
scope.$eval(propertyExrpession, data[i])
}

  一 . 5 响应数据的变化

  很多时候,当我们刷新数据时,我们会发现"绑定"到指令上的数据并没有发生相应的变化,这是为什么呢?原因是,很多时候我们所"绑定"的是指令在初始化时scope上的数据(除非我们tempalte中使用了ng-repeatng-model等数据绑定指令)当应用运行起来时,并且我们没有为指令添加监听器的时候,指令上的数据便不会随着Model的数据改变而改变了。让我们来添加监听器吧

var watchFn = function(watchScope){
return watchScope.$eval(propertyExpression, data[index]);
}
scope.$watch(watchFn, function(newVal, oldVal){
itemElement.text(newVal);
})

  我们看到,在指令内部我们使用scope.$watch对一个函数进行监听(在一些情况下也可以对某个scope上的对象进行监听并执行对应的回调函数)。这里有几个值得注意的地方:1 . 使用$watch对一个函数进行监听,该函数会有一个参数(此处为watchScope)来表达监听者的作用域 。2 . 我们为什么要对一个函数,而不是某个值进行监听呢?因为这个函数所return的就是一个会改变的值。3 . 什么时候,这个会改变的值会进行变化呢?答案是当$eval函数上的第二个参数data发生改变时(在AngularJS中,当作用域的某个属性发生变化时,以它为依赖的函数也会重新进行计算。也就是说$scope.abc发生了变化,那么function someFun ($scope.abc){...} 也会重新计算一遍)。因为$eval重新运算因此其return的值也就有可能发生变化了。通过以上3点,我们可以使指令实时地绑定的,而不再是绑定指令初始化阶段的数据了

   二 . 6 闭包

  闭包带来的问题这里就不多说了,我们应该使用"立即调用的函数表达式"(IIFE)来解决这个问题。

for(var i = 0; i < data.length; i++){
(function(){//建立IIFE
var index = i;
var someFun = function(index){
//...
}
}())
}

三、创建复杂指令

   三 . 0 templateUrl

  在指令内部return的对象里有一个templateUrl属性,我们可以通过为它指定一个函数,来return变换不同的模板。同理,这个技巧也适用于ng-include

templateUrl: function(attrs){
return attrs["template"] = "show" ? "show.html" : "index.html";
} <div ng-include="{{ choiceTemplate(name) }}">
//
$scope.choiceTemplate = function(name){
return name == "index" ? "index.html" : "other.html"
}

  三 . 1 指令与控制器的作用域

  默认情况下,链接函数会被传入控制器的作用域中,而控制器所管理着的视图会包含指令所要用到的元素(多读几遍就懂啦O(∩_∩)O)。同时,指令会继承该作用域的所有属性和方法。并且每个指令实例和作用域之间会相互影响(注:图中的箭头都是双箭头,表明它们之一所发生的变化会波及到其它)。若这并不是我们想要的,我们应该如何把作用域分离出来呢?

  三 . 2 为指令实例创建独立作用域

    如图,我们解除了每个实例之间的影响(注:图中箭头都变成了单箭头),实例也不再能影响作用域上的同名属性。这是我们通过设置scope: true来实现的。简单来说,就是每个指令实例都会继承到控制器作用域上的所有方法和属性,但是这些方法和属性是每个指令实例所私有的,同名属性之间不能再相互影响

  三 . 3 为指令实例创建隔离作用域

    如图,指令实例和实例之间、指令和控制器作用域之间没有任何关系了,这就是隔离的含义:不再继承来自控制器作用域上的任何方法和属性,同时指令实例之间也不能相互影响了。

  三 . 4 隔离作用域下的数据绑定

    三 . 4 . 1 隔离作用域下的单向数据绑定

    那我们如何做到:指令实例和控制器之间、指令实例之间不相互影响,但同时又能继承控制器作用域上某些我们想要得到的属性或者方法呢?AngularJS为我们提供了这样一种方法:通过为包含指令的元素上设置属性来传输数据。

      (注:图中的箭头都是单箭头) 我们通过 '@'符号,为数据进行单向绑定(指令实例继承来自控制器作用域的属性,但在指令中该同名属性的变化不会影响到控制器作用域的同名属性)。同时,我们应该注意:在视图中属性的值是要先用解析器{{  }}来解析出我们所要绑定的值,然后再通过指令内部的attrs对象来传递该值。

//视图中
<div scope-demo nameprop="{{data.name}}"></div>
//指令内部
scope{
local: '@nameprop'
}
//指令模板template内部
<p>{{local}}</p>

    三 . 4 . 2 隔离作用域下的双向数据绑定

    那我们如何做到:指令实例和控制器之间能够相互影响,同时又能继承控制器作用域上某些我们想要得到的属性或者方法呢?答案是使用"="符号

      注:图中箭头为双箭头。我们通过 '='符号,为数据进行双向绑定(指令实例继承来自控制器作用域的属性,同时指令实例A中的同名属性的变化会影响到控制器作用域的同名属性,然后控制器作用域继续影响其他指令实例),也就是说,多个指令实例和控制器作用域之间通过attrs重新相互影响了。同时,我们应该注意:在视图中属性的值是不需要使用解析器{{  }}来解析出我们所要绑定的值,只需要填写我们所需要传递的属性的名称。

//视图中
<div scope-demo nameprop="data.name"></div>
//指令内部
scope{
local: '=nameprop'
}
//指令模板template内部
<p>{{local}}</p>

    三 . 4 . 3 隔离作用域下计算表达式

    那我们如何继承来自控制器作用域上的方法呢?答案是通过"&"符号

//视图中
<div scope-demo nameprop="data.name" city="getCity()"></div>
//指令内部
scope{
local: '=nameprop' ,
getCity: '&city'
}
//指令模板template内部
<p>{{local}}</p>
<p>{{getCity()}}</p>

    三 . 4 . 3 . 1 使用隔离作用域的数据来计算表达式

    那我们如何把指令实例上的隔离作用域上的属性当做参数传递给我们指令所继承而来的函数呢?

//视图中
<div scope-demo="{{name}}" city="getCity(newValue)"></div>
//指令内部
scope: {
local: '@scopeDemo',
cityFn: '&city'
}
//指令模板内部
template: "<div>{{cityFn( {newValue: local} )}}</div>",

    注意:1. 在调用函数的内部我们通过 {newValue: local} 为指令实例隔离作用域上的属性当做是参数传递给我们继承而来的方法。2. 在视图中的函数的参数的名字不能与控制器作用域上的属性值重名,因为这样子会把错误的参数传递给目标函数。3. 视图中函数的参数名要与指令内部模板调用函数时所使用的参数名称一致

    三 . 5 个人建议

    我们可以把某些特定的数据与方法,放在包含指令的控制器作用域中,然后根据场景来构造不同的作用域绑定效果,从而降低在指令中的代码量

  

  

  参考资料

  《AngularJS高级程序设计》P357

AngularJS中的指令的更多相关文章

  1. AngularJS中的指令全面解析(转载)

    说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...

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

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

  3. Angularjs 中使用指令绑定点击事件

    项目中,模板中的菜单是jQuery控制的,在Angularjs中就运行不到了,因为菜单项是ng-repeat之后的. 如html <ul id="main-menu"> ...

  4. 浅谈AngularJS中的指令和指令间的相互通信

    说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...

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

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

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

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

  7. angularJS中自定义指令

    学习了angularJS一周,但是大部分时间被自定义指令占用了.博主表示自学互联网好心塞的,发现问题的视觉很狭窄,这比解决问题要更难.这篇文章首先介绍了自定义,然后介绍了在使用自定义指令遇到的问题. ...

  8. angularjs学习之六(angularjs中directive指令的一般编程事件绑定 模板使用等)

    angular js 中模板的使用.事件绑定以及指令与指令之间的交互 相应教学视频地址(需FQ):v=aG8VD0KvUw4">angularjs教学视频 <!doctype h ...

  9. Angularjs中编写指令模版

    angular.module('moduleName', []).directive( 'namespaceDirectiveName', [ function() { return { restri ...

随机推荐

  1. 【代码笔记】iOS-淡出淡入效果

    一,效果图. 二,工程图. 三,代码. ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UIVie ...

  2. get和post的区别与乱码问题解决

    ★ get和post的区别:     1.get请求通过url地址发送请求参数,可以在地址栏上直接显示     2.post请求通过请求体发送请求参数,不会再地址栏上显示     3.get在地址栏显 ...

  3. 文件件监听器,android系统拍照功能调用后删除系统生成的照片

    先说说要实现的功能: android调用系统拍照功能实时 预览 删除 上传 保存 (用户不能再本地文件夹中看到拍的照片) 再说说遇到的问题: 1.调用系统拍照在系统自带的拍照文件夹中生成一张随机命名图 ...

  4. C#编写抽奖问题

    输入每个人的中奖号码,进行滚动显示    //清屏  //随即一个索引   // 根据索引打印  //等待0.1秒            Console.Write("请输入参与者人数:&q ...

  5. 挖一挖C#中那些我们不常用的东西之系列(5)——FlagAttribute

    说到FlagsAttribute,源自前几天看到了一小段代码,大概意思就是根据航班政策来返回哪些配送方式是否可用,根据这些是否可用 来隐藏或者开启界面的相关配送方式,如果大家订过机票可能知道配送方式有 ...

  6. java 中的volatile

    本博客摘录自   http://www.infoq.com/cn/articles/java-memory-model-4/ 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解 ...

  7. Windows自动关机命令

    winxp中自带了自动关机功能,在开始→运行中使用SHUTDOWN命令 1. 延迟关机关机 shutdown -s -t 120 -s为关机:-t为时间,以秒为单位,120表示2分钟 表示两分钟后关机 ...

  8. 说一下output子句

    Output子句日常灰常有用,而且用的地方也挺多,但是确好多时候被我们忽视,今天我就也简单扫盲一下这个语句的用法. Output子句 返回受 INSERT.UPDATE.DELETE 或 MERGE ...

  9. C#调用SQL Server参数过程传参

    -SQL SERVER生成测试环境: Create database Test; go USE [Test] GO if OBJECT_ID('Tab2','U') is not null drop ...

  10. Python基础之生成器

    1.生成器简介 首先请确信,生成器就是一种迭代器.生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中.另外,对于生成器的特殊语法支持使得编写一个生成器比 ...