12

我想知道是否可以在评估指令的所有(或仅部分)属性(没有隔离范围)之后只执行一次回调。属性非常适合将配置传递给指令。问题是您可以分别观察每个属性并多次触发回调。

在示例中,我们有一个没有独立作用域的指令,它观察两个属性:姓名和姓氏。在任何更改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在这里

所以效果是,一键更改姓名和姓氏后,action会触发两次回调:

name: 
surname: Brown

name: John
surname: Brown

所以问题是:action只能在姓名和姓氏值都更改的情况下触发一次?

4

5 回答 5

12

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

IE

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

这将在所有$digest周期上运行,并且如果$watch检测到返回数组(或者您想要构造函数的返回值)与旧值不匹配,则回调参数$watch将触发。但是,如果您确实使用对象作为返回值,请确保将true值保留在最后一个参数中,$watch以便$watch进行深度比较。

于 2013-10-17T21:46:12.523 回答
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');
    }
 }
}]);

这里

于 2013-10-22T20:02:44.180 回答
0

下划线(或 lo-dash)有一个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);
    }
 }
});
于 2013-10-17T22:43:00.087 回答
0

有几种方法可以解决这个问题。我非常喜欢去抖动解决方案。但是,这是我对这个问题的解决方案。这将所有属性组合在一个属性中,并创建您感兴趣的属性的 JSON 表示。现在,您只需要 $observe 一个属性并具有良好的性能!

这是带有实现的原始 plunkr 的一个分支:http: link//plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ

于 2016-09-26T07:50:44.730 回答
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();
});
于 2015-08-13T12:35:10.613 回答