我想知道在评估指令的所有(或仅一些)属性(没有隔离范围)之后,只能执行一次回调。将配置传递给指令真的很棒。问题是你可以分别观察每个属性并多次触发回调。
在示例中,我们有一个没有隔离范围的指令,它遵循两个属性:name和surname。在任何更改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
只能被解雇一次吗?
答案 0 :(得分:12)
您可以使用$watch
来评估自定义函数,而不是特定模型。
即
$scope.$watch(function () {
return [$attrs.name, $attrs.surname];
}, action, true);
这将在所有$digest
周期运行,并且如果$watch
检测到返回数组(或者您想要构造函数的返回值)与旧值不匹配,则回调参数到$watch
会发射。如果您确实使用对象作为返回值,请确保将true
值保留为$watch
的最后一个参数,以便$watch
进行深度比较。
答案 1 :(得分: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 :(得分: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
答案 3 :(得分:0)
我解决了与其他方法完全相同的问题,尽管我正在寻找不同的想法。虽然cmw的建议正在发挥作用,但我将其性能与我的相比较,并且看到$ watch方法被调用的次数太多了,所以我决定按照我实施的方式保存。
我为我想跟踪的两个变量添加了$ observe调用,并将它们绑定到去抖动调用。由于它们都以很小的时间差进行修改,因此两个$ observe方法都会触发相同的函数调用,这会在短暂的延迟后执行:
var debounceUpdate = _.debounce(function () {
setMinAndMaxValue(attrs['minFieldName'], attrs['maxFieldName']);
}, 100);
attrs.$observe('minFieldName', function () {
debounceUpdate();
});
attrs.$observe('maxFieldName', function () {
debounceUpdate();
});
答案 4 :(得分:0)
有几种方法可以解决这个问题。我很喜欢去抖动解决方案。但是,这是我解决这个问题的方法。这将所有属性组合在一个属性中,并创建您感兴趣的属性的JSON表示。现在,您只需要观察一个属性并获得良好的性能!
这是原始plunkr的一个分支,具有实现:
link
http://plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ