欢迎大家讨论与指导 : )
明天天会继续更新本文O(∩_∩)O
前言
当AngularJS中的内置指令不能满足我们的需求,或者当我们需要创建一个能够用于多个AngularJS程序的自包含的功能单元时,我们应该创建自定义指令来满足需求。
一、创建自定义指令
一 . 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上的数据。当应用运行起来时,并且我们没有为指令添加监听器的时候,指令上的数据便不会随着Model的数据改变而改变了。让我们来添加监听器吧
var watchFn = function(watchScope){ return watchScope.$eval(propertyExpression, data[index]); } scope.$watch(watchFn, function(newVal, oldVal){ itemElement.text(newVal); })
我们看到,在指令内部我们使用scope.$watch对一个函数进行监听。这里有几个值得注意的地方: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){ //... } }()) }
二、创建复杂指令
--------明天更新-------
参考资料
《AngularJS高级程序设计》P357