Обработка 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" для обработки кода с одним кликом.
Не знаю, возможно ли это, но я считаю это очень полезным для всех.
Не стесняйтесь делиться своими мнениями!
Ответы
Ответ 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>