2013-10-17 42 views
11

不知道是否可以在评估指令的全部(或仅某些)属性(没有隔离范围)后仅执行一次回调。属性非常适合将配置传递给指令。事情是你可以分别观察每个属性并多次触发回调。

在这个例子中,我们有一个没有独立范围的指令,它观察到两个属性:姓名和姓氏。任何更改后action回调触发:

HTML

<button ng-click="name='John';surname='Brown'">Change all params</button> 
<div person name="{{name}}" surname="{{surname}}"></div> 

JS

angular.module('app', []). 

directive('person', function() { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     $attrs.$observe('name', action); 
     $attrs.$observe('surname', action); 
    } 
} 
}); 

Plunker here

所以效果是,在点击更改姓名后,action回调触发了两次:

name: 
surname: Brown 

name: John 
surname: Brown 

所以问题是:action只有一次既姓名值改变解雇?

回答

11

您可以使用$watch来评估自定义函数而不是特定模型。

$scope.$watch(function() { 
    return [$attrs.name, $attrs.surname]; 
}, action, true); 

这将在所有$digest周期中运行,如果$watch检测返回数组(或无论你想构建你的函数的返回值)不匹配的旧值,则回调参数$watch将会触发。如果确实使用对象作为返回值,请确保将最后一个参数的true值保留为$watch,以便$watch执行深度比较。

+0

太谢谢你了! – iamtankist

0

下划线(或左划线)具有once函数。如果你在once中包装你的功能,你可以确保你的功能只被调用一次。

angular.module('app', []). 

directive('person', function() { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     var once = _.once(action); 
     $attrs.$observe('name', once); 
     $attrs.$observe('surname', once); 
    } 
} 
}); 
+2

不幸的是'_.once'只会触发一次回调,但只会改变第一个属性。我的意思是,在单个“$摘要”阶段进行所有更改后,应该触发回调。 – kseb

0

所以,我结束了我自己的实现observeAll方法,它可以等待一个调用堆栈中的属性的一些变化的。它的工作原理,但我不知道性能。

@cmw的解决方案似乎更简单,但是当对象相等性评估许多次时,性能会受到大量参数和多个$ digest阶段运行的影响。但是我决定接受他的回答。

下面你可以找到我的方法:

angular.module('utils.observeAll', []). 

factory('observeAll', ['$rootScope', function($rootScope) { 
    return function($attrs, callback) { 
     var o = {}, 
      callQueued = false, 
      args = arguments, 

      observe = function(attr) { 
       $attrs.$observe(attr, function(value) { 
        o[attr] = value; 
        if (!callQueued) { 
         callQueued = true; 
         $rootScope.$evalAsync(function() { 
          var argArr = []; 
          for(var i = 2, max = args.length; i < max; i++) { 
           var attr = args[i]; 
           argArr.push(o[attr]); 
          } 
          callback.apply(null, argArr); 
          callQueued = false; 
         }); 
        } 
       }); 
      }; 

     for(var i = 2, max = args.length; i < max; i++) { 
      var attr = args[i]; 
      if ($attrs.$attr[attr]) 
       observe(attr); 
     } 
    }; 
}]); 

而且你可以在你的指令中使用它:

angular.module('app', ['utils.observeAll']). 

directive('person', ['observeAll', function(observeAll) { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     observeAll($attrs, action, 'name', 'surname'); 
    } 
} 
}]); 

Plunker here

0

我下定决心,我不得不使用完全相同的问题另一种方法,虽然我正在寻找不同的想法。尽管cmw的建议正在起作用,但我将它与我的表现进行了比较,并且发现$ watch方法被调用的次数太多了,所以我决定按照我实施的方式进行。

我添加了$ observe调用我想跟踪的两个变量并将它们绑定到一个去抖动调用。因为他们都被修改用很少的时间差,无论是$观察方法触发相同的函数调用,它得到一个短暂的延迟后执行:

var debounceUpdate = _.debounce(function() { 
    setMinAndMaxValue(attrs['minFieldName'], attrs['maxFieldName']); 
}, 100); 

attrs.$observe('minFieldName', function() { 
    debounceUpdate(); 
}); 

attrs.$observe('maxFieldName', function() { 
    debounceUpdate(); 
}); 
0

有提出解决这个问题的几种方法。我很喜欢去抖解决方案。但是,这是我解决这个问题的方法。这将所有属性合并到一个属性中,并为您感兴趣的属性创建一个JSON表示。现在,您只需要$观察一个属性并且具有良好的性能!

原来这里是plunkr与实施叉子: linkhttp://plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ