$watch 监视 class 指令延迟 1 个周期的变化

$watch to monitor change in class on directive firing 1 cycle late

我的目标是在特定指令通过 ng-class 收到 class 'focused' 时执行一些操作。 但是观察每个指令的变化似乎在做我不明白的奇怪事情。首先是代码:

http://plnkr.co/edit/6Te8O2mlBI058DpwGF7f?p=preview

index.html

   <test-question
      ng-class="{'focused': tracer.focus == 1}"
      focusnum = "{{n}}"
      ng-click="tracer.focus = 1"
      >
      Test Question 1
    </test-question>
    <test-question
      ng-class="{'focused': tracer.focus == 2}"
      focusnum = "{{n}}"
      ng-click="tracer.focus = 2"
      >
      Test Question 2
    </test-question>
    <test-question
      ng-class="{'focused': tracer.focus == 3}"
      focusnum = "{{n}}"
      ng-click="tracer.focus = 3"
      >
      Test Question 3
    </test-question>

script.js

angular.module('sampleApp', [])
  .controller("SampleController", function($scope) {
    $scope.tracer = {
        focus: 1
    }
  })
  .directive('testQuestion', function() {
    return {
        restrict: 'E',
        link: function($scope, el, attrs) {
          $scope.$watch(function() {
                  return el.attr('class');
              }, function(newValue, oldValue) {
                  // do something here when directive gets class focused
                  console.log("text", el.text());
                  console.log("newValue", newValue);
                  console.log("oldValue", oldValue);
            });
        }
    }
  });

最初第一个测试问题被突出显示。当您单击第二个问题时,我希望控制台记录

"text" Test Question 1 "newValue" undefined "oldValue" focused "text" Test Question 2 "newValue" focused "oldValue" undefined

然而控制台实际记录

"text" Test Question 1 "newValue" focused "oldValue" undefined

因此,由于这种奇怪的行为,我无法准确检测指令何时收到 class 焦点。看起来手表好像晚了一个周期。如果有朋友能帮助我理解为什么会这样,那就太好了。谢谢!

额外

单击问题 2 后,如果我随后单击问题 3,控制台会记录

"text" Test Question 1 "newValue" "oldValue" focused "text" Test Question 2 "newValue" focused "oldValue" undefined

是的已经晚了,目前还没有针对这种情况的合适解决方案。但是您可以执行以下操作。

    $scope.$watch(function() {
              return el.hasClass('focused');
          }, function(newValue, oldValue) {
              // now you should use javascript setTimeout for check after $diggest
              var myOldValue = newValue; //this mean newValue realy oldValue yet.
               setTimeout(function(){
                 var myNewValue= el.hasClass('focused');
                  console.log(myNewValue, myOldValue)
                  //do staff here
               },0)
              //note that, don't use angular $timeout it may couse recursive stack
        });

为什么?

因为您正在观看 AngularJS 之外的异步事件。即需要在最后调用,通过使用 $scope.$apply() 告诉 AngularJS 刚刚发生了一个异步事件。

这就像我们的自定义 $('button').on('click', ...) 事件需要 $ scope.$apply().但是你可能会问,我正在观察使用 ng-class 所做的更改。这是正确的,但是在 ng-class 完成它的工作之前调用了你的观察函数。也就是说,您不是在看“ng-class”,而是在看 class 属性。出于这个原因,您正在收听的更改将在下一个摘要中注意到 angular。

如果您尝试访问观察器中的当前 class,您将获得旧值,因为 ng-class 尚未完成其工作。

如果您尝试访问当前的 class javascript setTimeout 函数,您将获得正确的值,因为 setTimeout 将在所有 angular 工作完成后调用。

此案例适用于 AngularJS 以外的所有更改。当你试图观察一个元素的高度、宽度、颜色、一个对象 属性 等时,你可以安静地使用它。

注意 想想看 AngularJS 之外的变化就像一个承诺,setTimeout 将是你的回调。