角度指令多次射击

时间:2017-08-16 11:41:55

标签: angularjs

我是AngularJS的新手,并且有一个问题,我希望有人可以指出我正确的方向来搞清楚。我创建了一个名为sizeWatcher的指令,该指令作为属性放置在HTML中,它基本上只是获取它所放置的元素的高度,并将该高度回显到名为style的范围变量中我通过使用ng-style="style"设置了另一个元素。

我发现每当我打开手风琴时,$watch就会触发指令,但它会多次发射。我的console.log中有一个$watch并且看到3个日志条目,前两个是相同的(猜测这是在手风琴打开之前点击发生的,然后手风琴打开,第3个日志条目是手风琴打开后的最终高度)。主要问题是style变量仅在手风琴扩展之前被设置为较小的高度,即使日志正在注册指令被击中的最大高度 - 我怎么能忽略第一次$watch事件发生,并且只对指令的最后和最后一次执行采取相应行动?任何对此的见解将不胜感激。相关代码如下:

<小时/> 的 TEMPLATE:

 <div class="content-wrap" id="height-block" ng-style="style">
     <!-- Other HTML etc... -->
     <uib-accordion size-watcher close-others="oneAtATime">
          <!-- Accordion Directive HTML.. -->
     </uib-accordion>
 </div>

JavaScript的:

.directive("sizeWatcher", function () { //add size-watcher attribute to element on the page to have it echo its' height to the {{style}} scope
    function link(scope, element, attrs) {
        scope.$watch(function () { //watch element for changes
            var height = element[0].offsetHeight;
            console.log(height);
            if (height > 150) {
                scope.style = {
                    height: height + 'px'
                };
            }
        });
    }
    return {
        restrict: "AE", //attribute & element declarations
        link: link
    };
})

3 个答案:

答案 0 :(得分:2)

  

我如何忽略第一个$ watch事件的发射并且只采取行动   因此,该指令的最后和最后一次贯穿?

当新值或旧值为undefined且不相等时,您可以忽略观察者:

$scope.$watch(function () {
    return element.height(); // or something else
},
function (newVal, oldVal) {

   if (newVal !== undefined && oldVal !== undefined && newVal !== oldVal) {
         // your stuff   
         if (newVal > 150) {
            scope.style = {
                height: newVal + 'px'
            };
        }    
   }                
});

无论如何,你可以使用if语句来满足你的需求

仅供参考,提高效果$watch会返回取消回拨,因此您可以随时停止观察:

var cancelWatch = $scope.$watch(function () {
    return element.height();
},
function (newVal, oldVal) {        
    if (<some condition>) {            
        cancelWatch();            
    }        
});

答案 1 :(得分:0)

显然,对此的回答需要指向Angular-Documentation for $watch;)

的链接

它陈述如下:

  

在观察者注册范围后,listener fn被异步调用(通过$evalAsync)来初始化观察者。在极少数情况下,这是不合需要的,因为在watchExpression的结果没有改变时调用了监听器。要在listener fn中检测此方案,您可以比较newValoldVal。如果这两个值相同(===),则由于初始化而调用了侦听器。

这可能解释了你的第一次电话。

我猜测第二次调用是因为手风琴在初始化后被重新渲染(带有标题/或标签或任何东西),触发$digest,从而触发高度上的$watch表达式。 / p>

最后,当您打开手风琴并且高度实际发生变化时,会发生第三次调用。

要解决这个问题,你可以比较一下观察到的表达式的newValue和oldValue,比如Maxim Shoustin在他的回答中所说。这是一个例子(再次来自Angular-docs)

scope.$watch(
  // This function returns the value being watched. It is called for each turn of the $digest loop
  function() { return food; },
  // This is the change listener, called when the value returned from the above function changes
  function(newValue, oldValue) {
    if ( newValue !== oldValue ) {
      // Only increment the counter if the value changed
      scope.foodCounter = scope.foodCounter + 1;
    }
  }
);

但是,如果您确实想要更改元素的样式,可能需要查看ng-class而不是手动注册任何观察者!

答案 2 :(得分:0)

这种情况正在发生,因为你没有使用$ watch正确方式,

  • $ watch的第一个参数是您要观看的变量(这可以是回调)。
  • $ watch的第二个参数是一个回调,它在更改时执行所需的操作

所以在你的情况下,它会是这样的

scope.$watch(
   function () {
       return element[0].offsetHeight;
   },
   function () { //watch element for changes
      var height = element[0].offsetHeight;
      console.log(height);
      if (height > 150) {
          scope.style = {
              height: height + 'px'
          };
      }
   }
)

请注意第一个函数,因此每当它返回的值发生变化时,第二个回调将执行

希望这有助于你