Подтвердить что ты не робот

Обработка 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): может ли кто-нибудь более опытный написать какую-нибудь красивую директиву для обработки этих событий?

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

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

4b9b3361

Ответ 1

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

<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
              }
          });
        }
    };
}])

Здесь пример

Plunker

Ответ 2

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

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

Я считаю, что $apply начнет цикл еще одного цикла, который оставит $eval s. $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.

Ответ 3

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

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

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

Учитывая

<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

    });
};

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

Ответ 4

Я столкнулся, пытаясь найти способ обработки двойного щелчка и щелчок одновременно. Я использовал концепции здесь, чтобы отменить первоначальный щелчок. Если перед задержкой происходит второй клик, выполняется действие двойного щелчка. Если нет второго щелчка, после того, как задержка закончена, действие 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)
      })

    }
  }

})

Ответ 5

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

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