在ng-mousedown中绑定文档mouseup



我想要一些从ng-mousedown开始到mouseup结束的东西。我还希望在鼠标离开元素时,当鼠标按钮保持按下时继续发生一些事情。在普通Javascript中实现此功能的一种非常常见的方法是将文档mouseup事件绑定到元素mousedown事件中,如下所示:

HTML:

<button onMouseDown='onMouseDown()'>Click Me</button>
<div id="val"></div>

JS:

function onMouseDown() {
  var el = document.querySelector('#val'),
      changeValue = function() {
        el.innerHTML = 'done!';
        document.removeEventListener('mouseup', changeValue);
      };
  
  el.innerHTML = 'waiting for mouseup...';
  document.addEventListener('mouseup', changeValue);
}

我在使用jQuery的one(), angular.element.oneaddEventListener/removeEventListener在Angular中实现此行为时遇到了麻烦:

HTML:

<div ng-app="MyApp" ng-controller="AppCtrl">
  <div>Note that 'done!' is not rendered, despite 'mouseup' being logged to the console</div>
  <button ng-mousedown='changeValue()'>Click Me</button>
  <div>{{value}}</div>   
</div>

JS (jQuery的one()):

angular
  .module('MyApp', [])
  .controller('AppCtrl', function($scope) {
    $scope.changeValue = function() {
      console.log('mousedown');
      $scope.value = 'waiting for mouseup...';  
      
      $(document).one('mouseup', function() {
        console.log('mouseup');
        $scope.value = 'done!';
      });
    }
  });

JS (angular.element.one):

angular
  .module('MyApp', [])
  .controller('AppCtrl', function($scope) {
    $scope.changeValue = function() {
      console.log('mousedown');
      $scope.value = 'waiting for mouseup...';  
      
      angular.element(document).one('mouseup', function() {
        console.log('mouseup');
        $scope.value = 'done!';
      });
    }
  });

JS (addEventListener/removeEventListener):

angular
  .module('MyApp', [])
  .controller('AppCtrl', function($scope) {
    $scope.changeValue = function() {
      var changeValue = function() {
        console.log('mouseup');
        $scope.value = 'done!';
        document.removeEventListener('mouseup', changeValue);
      };
      
      console.log('mousedown');
      $scope.value = 'waiting for mouseup...';  
      
      document.addEventListener('mouseup', changeValue);
    }
  });

我也试着像这样利用ng-mouseupng-mouseleave:

HTML:

<div ng-app="MyApp" ng-controller="AppCtrl">
  <div>While clicking the button, move the mouse off of the button and release the mouse button. Note that 'done!' is not rendered, despite 'mouseup' being logged to the console</div>
  <button ng-mousedown='mouseDown()' ng-mouseup="mouseUp()" ng-mouseleave="mouseLeave()">Click Me</button>
  <div>{{value}}</div>  
</div>

JS:

angular
  .module('MyApp', [])
  .controller('AppCtrl', function($scope) {
    $scope.mouseDown = function() {
      console.log('mousedown');
      $scope.value = 'waiting for mouseup...';  
    }
    $scope.mouseUp = function() {
      console.log('mouseup');
      $scope.value = 'done!';
    }
    $scope.mouseLeave = function() {
      if ($scope.value && $scope.value.indexOf('waiting') > -1) {
        console.log('mouseleave, binding mouseup'); 
        $(document).one('mouseup', function() {
           $scope.mouseUp();
        });
      }
    }
  });

结果是一样的。$scope.mouseUp被执行,但'done!'从未被渲染。这会给$scope.$watch带来额外的问题:

HTML:

<div ng-app="MyApp" ng-controller="AppCtrl">
  <div>value never === 'done!' in $scope.$watch</div>
  <button ng-mousedown='changeValue()'>Click Me</button>
  <div>{{value}}</div>   
</div>

JS:

angular
  .module('MyApp', [])
  .controller('AppCtrl', function($scope) {
    $scope.changeValue = function() {
      console.log('mousedown');
      $scope.value = 'waiting for mouseup...';  
      
      $(document).one('mouseup', function() {
        console.log('mouseup');
        $scope.value = 'done!';
      });
    }
    $scope.$watch('value', function(value) {
      if (value === 'done!') {
        console.log('value set to "done!"'); 
      }
    });
  });

不知道为什么这个问题一个多月没人回答。

这里的主要问题很简单——更新$scope.value的代码是在AngularJS外部执行的。为了使您的数据绑定工作,您应该像这样用$scope.$apply包装它:
$scope.$apply(function () {
  $scope.value = 'done!';                
});

请注意,ng-mouseup工作正常,您可以看到,当鼠标向上触发按钮上方。但是如果你把鼠标移到按钮外面,就会发生$(document).one('mouseup'..)事件。

只要记住,你必须包装所有的外部回调与$scope.$apply (JQuery HTTP回调,DOM事件绑定不与ng-*指令,setTimeout等),如果你需要它与AngularJS绑定或观察者工作。但是要三思——在另一个$scope.$apply中调用$scope.$apply会导致错误。

您可以查看文档或在这里阅读更多信息

最新更新