it-swarm.com.ru

Обработка ng-click и ng-dblclick на одном элементе с AngularJS

Я искал обработку событий как одиночного, так и двойного щелчка с AngularJS, поскольку AngularJS всегда запускает только событие ng-click, даже если для нашего элемента установлена ​​директива ng-dblclick.

Вот некоторый рабочий код для тех, кто ищет решение:

JS:

function MyController($scope) {

    var waitingForSecondClick = false;
    $scope.singleClickAction = function() {

        executingDoubleClick = false;
        if (waitingForSecondClick) {
            waitingForSecondClick = false;
            executingDoubleClick = true;
            return $scope.doubleClickAction();
        }
        waitingForSecondClick = true;

        setTimeout(function() {
            waitingForSecondClick = false;
            return singleClickOnlyAction();
        }, 250); // delay

        /*
         * Code executed with single AND double-click goes here.
         * ...
         */

        var singleClickOnlyAction = function() {
            if (executingDoubleClick) return;

            /*
             * Code executed ONLY with single-click goes here.
             * ...
             */

        }

    }

    $scope.doubleClickAction = function() {

        /*
         * Code executed ONLY with double-click goes here.
         * ...
         */

    };

}

HTML:

<div ng-controller="MyController">
    <a href="#" ng-click="singleClickAction()">CLICK</a>
</div>

Итак, мой вопрос (поскольку я новичок в AngularJS): может ли кто-нибудь более опытный написать какую-нибудь директиву Nice для обработки этих обоих событий?

На мой взгляд, идеальным способом было бы изменить поведение ng-click, ng-dblclick и добавить некоторую директиву «ng-sglclick» для обработки кода одним щелчком мыши. Не знаю, возможно ли это, но я считаю, что это очень полезно для всех.

Не стесняйтесь делиться своим мнением!

36
stealz

Вы можете просто написать свой собственный. Я посмотрел на то, как угловые обрабатывали клик, и изменил его с помощью кода, который я нашел здесь: Jquery bind - двойной клик и один клик отдельно

<div sglclick="singleClick()" ng-dblClick="doubleClick()" style="height:200px;width:200px;background-color:black">


mainMod.controller('AppCntrl', ['$scope', function ($scope) {
    $scope.singleClick = function() {
      alert('Single Click');
    }
    $scope.doubleClick = function() {
      alert('Double Click');
    }
}])


mainMod.directive('sglclick', ['$parse', function($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attr) {
          var fn = $parse(attr['sglclick']);
          var delay = 300, clicks = 0, timer = null;
          element.on('click', function (event) {
            clicks++;  //count clicks
            if(clicks === 1) {
              timer = setTimeout(function() {
                scope.$apply(function () {
                    fn(scope, { $event: event });
                }); 
                clicks = 0;             //after action performed, reset counter
              }, delay);
              } else {
                clearTimeout(timer);    //prevent single-click action
                clicks = 0;             //after action performed, reset counter
              }
          });
        }
    };
}])

Вот пример

Плункер

27
Rob

Ответ Грега определенно ближе всего к самому чистому ответу. Я собираюсь основываться на его ответе, чтобы придумать версию, в которой не нужно писать новый код и не нужно использовать новые инъекции в вашем контроллере.

Первое, что нужно спросить, - почему для решения подобных проблем используются тайм-ауты. По сути, они используются, чтобы заставить функцию пропускать оставшуюся часть текущего цикла событий, чтобы выполнение было чистым. В Angular, однако, вы на самом деле заинтересованы в том, как работает цикл дайджеста. Он почти такой же, как ваш классический обработчик событий, за исключением некоторых незначительных отличий, которые делают его великолепным для UX. Некоторые инструменты, которые вы можете использовать для определения порядка выполнения функций, включают scope.$eval, scope.$evalAsync, scope.$apply и scope.$applyAsync.

Я считаю, что $applys запустит еще один цикл дайджеста, который оставляет $evals. $eval будет запускать любой код, который вы немедленно включите в контекст текущего $scope, а $evalAsync поставит вашу функцию в очередь в конце цикла дайджеста. По сути, $evalAsync является более чистой версией $timeout с одним большим отличием - первое имеет контекст и существует в области видимости!

Это означает, что вы можете обрабатывать ng-click и ng-dblclick на одном элементе. Заметим, однако, что это по-прежнему вызовет функцию одиночного щелчка перед функцией двойного щелчка. Этого должно быть достаточно:

<div ng-controller="MyController">
    <a href="#"
       ng-click="$evalAsync(singleClickAction())"
       ng-dblclick="doubleClickAction()">
       CLICK
    </a>
</div>

Вот jsfiddle с намеченной функциональностью, использующий Angular 1.6.4 .

24
stites

Наткнулся на это и подумал, что выкину альтернативу. Это не слишком отличается от оригинального плаката, кроме двух ключевых моментов.

1) Нет вложенных объявлений функций.

2) Я использую $ timeout. Я часто использую $ timeout даже без задержки ... особенно, если я выполняю обещания сделать другую работу. $ Timeout сработает при прохождении цикла дайджеста, что гарантирует применение любых изменений данных в области. 

Дано

<img src="myImage.jpg" ng-click="singleClick()" ng-dblclick="doubleClick()">

В вашем контроллере функция singleClick будет выглядеть так:

$scope.singleClick = function () {
    if ($scope.clicked) {
        $scope.cancelClick = true;
        return;
    }

    $scope.clicked = true;

    $timeout(function () {
        if ($scope.cancelClick) {
            $scope.cancelClick = false;
            $scope.clicked = false;
            return;
        }

        //do something with your single click here

        //clean up
        $scope.cancelClick = false;
        $scope.clicked = false;
    }, 500);
};

И функция doubleClick будет выглядеть нормально:

$scope.doubleClick = function () {

    $timeout(function () {

        //do something with your double click here

    });
};

Надеюсь, это поможет кому-то ...

13
Greg Grater

Я столкнулся, пытаясь найти способ справиться с двойным щелчком и щелчком одновременно. Я использовал концепции здесь, чтобы отменить исходный клик. Если второй щелчок происходит до задержки, выполняется действие двойного щелчка. Если второго щелчка нет, по истечении задержки запускается действие ngClick по умолчанию, и на элементе запускается исходное событие (и ему разрешается пузыриться, как это было бы изначально).

Пример

<div ng-click="singleClick()"><span double-click="doubleClick()">double click me</span></div>

Код

.directive('doubleClick', function($timeout, _) {

  var CLICK_DELAY = 300
  var $ = angular.element

  return {
    priority: 1, // run before event directives
    restrict: 'A',
    link: function(scope, element, attrs) {
      var clickCount = 0
      var clickTimeout

      function doubleClick(e) {
        e.preventDefault()
        e.stopImmediatePropagation()
        $timeout.cancel(clickTimeout)
        clickCount = 0
        scope.$apply(function() { scope.$eval(attrs.doubleClick) })
      }

      function singleClick(clonedEvent) {
        clickCount = 0
        if (attrs.ngClick) scope.$apply(function() { scope.$eval(attrs.ngClick) })
        if (clonedEvent) element.trigger(clonedEvent)
      }

      function delaySingleClick(e) {
        var clonedEvent = $.Event('click', e)
        clonedEvent._delayedSingleClick = true
        e.preventDefault()
        e.stopImmediatePropagation()
        clickTimeout = $timeout(singleClick.bind(null, clonedEvent), CLICK_DELAY)
      }

      element.bind('click', function(e) {
        if (e._delayedSingleClick) return
        if (clickCount++) doubleClick(e)
        else delaySingleClick(e)
      })

    }
  }

})
2
Jesse Houchins

Присоединяясь к частям ответов здесь:

  • используя стратегию @GregGrater для простоты
  • создание directive, как @Rob (тот, который принят в качестве лучшего ответа в этой теме)
  • решить проблему ответа @Rob, заменив встроенную директиву ngClick с помощью @EricChen answer

Здесь Plunker с сущностью идеи (такой же, как фрагмент в этом ответе; см. Ниже).

Помимо примечания: в идеале, если для элемента не определен ng-dblclick, он не должен предотвращать одиночный щелчок (здесь Plunker fork, реализующий эту идею)

(function(angular) {
  'use strict';
var myApp = angular.module('myApp', []);

myApp.controller('myCtrl', ['$scope', function($scope) {
  $scope.click = false;
  $scope.singleClick = function() {
    $scope.click = 'single';
  };
  $scope.doubleClick = function() {
    $scope.click = 'double';
 };
}]);

// remove the buildin ng-Click, to solve issue with https://stackoverflow.com/a/20445344/4352306
myApp.config(function($provide) { // Source: https://stackoverflow.com/a/23209542/4352306
  $provide.decorator('ngClickDirective', ['$delegate', function ($delegate) {
   //$delegate is array of all ng-click directive, in this case 
   // first one is angular buildin ng-click, so we remove it.
   $delegate.shift();
   return $delegate;
   }]);
});

// add custom single click directive: make ngClick to only trigger if not double click
myApp.directive('ngClick', ['$parse', '$timeout', dirSingleClickExclusive]); 

function dirSingleClickExclusive($parse, $timeout) {
  return {
    restrict: 'A',
    replace : false,
    priority: 99, // after all build-in directive are compiled
    link: link
  }
	
  function link ($scope, element, attrs) {
    const delay = 400;
    var clicked = false, cancelClick = false;
    var user_function = $parse(attrs['ngClick']); //(scope);
	
    element.on('click', function (e) {
      // Adapted from: https://stackoverflow.com/a/29073481/4352306
      if (clicked) cancelClick = true; // it is not a single click
      clicked = true;
      
      if (!cancelClick) { // prevent a second timeout
        $timeout(function () { // for time window between clicks (delay)
          if (cancelClick) {
            clicked = false; cancelClick = false;
            return;
          }
          $scope.$apply(function () {
            user_function($scope, {$event : e});
          });
  				
          // reset click status
          clicked = false; cancelClick = false;
        }, delay);
      }
    });
  }
}
})(window.angular);
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example - custom single click</title>
  
  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  <script src="app.js"></script>
  
</head>
<body ng-app="myApp">
  <div ng-controller="myCtrl">
   <button ng-click="singleClick()" ng-dblclick="doubleClick()">Click me!</button>
   <p ng-if="click">This was a {{click}} click.</p>
  </div>
</body>
</html>

1
rellampec

это также работает, если вызов singleClick на doubleClick не делает никакой ошибки 

<div 
    onclick="var scope = angular.element(this).scope(); scope.singleClick();"
    ng-click="null" 
    ng-dblclick="doubleClick()"
></div>
0
Sajjad Shirazy