【问题标题】:$observe multiple attributes at the same time and fire callback only once$同时观察多个属性并且只触发一次回调
【发布时间】:2013-10-26 12:28: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);
    }
 }
});

Plunkerhere.

所以效果就是一键改名改姓后,action回调被触发了两次:

name: 
surname: Brown

name: John
surname: Brown

所以问题是:action 可以只被触发一次,同时更改姓名和姓氏值吗?

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      下划线(或低破折号)具有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);
          }
       }
      });
      

      【讨论】:

      • 不幸的是,_.once 只会触发一次回调,但只会在第一个属性更改时触发。我的观点是,应该在单个 $digest 阶段的所有更改之后触发回调。
      【解决方案3】:

      所以,我最终实现了自己的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');
          }
       }
      }]);
      

      Plunkerhere

      【讨论】:

        【解决方案4】:

        我解决了与使用另一种方法完全相同的问题,尽管我正在寻找不同的想法。当 cmw 的建议奏效时,我将其性能与我的进行了比较,发现 $watch 方法被调用了太多次,所以我决定保持我实现的方式。

        我为我想要跟踪的两个变量添加了 $observe 调用,并将它们绑定到 debounce 调用。由于它们都以非常小的时间差进行修改,因此两个 $observe 方法都会触发相同的函数调用,该函数调用会在短暂的延迟后执行:

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-12-04
            • 2016-04-20
            • 1970-01-01
            相关资源
            最近更新 更多