2013-10-17 80 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