javascript - doble - Manejando ng-click y ng-dblclick en el mismo elemento con AngularJS
double click event angular 4 (6)
La respuesta de Greg es definitivamente la más cercana a la respuesta más limpia. Voy a aprovechar su respuesta para encontrar una versión en la que no se deba escribir ningún código nuevo y no se necesiten nuevas inyecciones en su controlador.
Lo primero que debe preguntarse es por qué los tiempos de espera se utilizan para hackear este tipo de problemas. Esencialmente, se usan para hacer que una función omita el resto del ciclo de eventos actual para que la ejecución sea limpia. En angular, sin embargo, en realidad está interesado en cómo funciona el ciclo digest. Es casi lo mismo que su controlador de eventos clásico, a excepción de algunas diferencias menores que lo hacen ideal para UX. Entre las herramientas que tiene a mano para desordenar el orden de ejecución de la función se incluyen el scope.$eval
, scope.$evalAsync
, scope.$applyAsync
y scope.$applyAsync
.
Creo que $apply
s iniciará otro ciclo digest, que deja los $eval
s. $eval
ejecutará el código que incluya inmediatamente con el contexto del $scope
actual y $evalAsync
pondrá en cola su función para que se ejecute al final del ciclo de resumen. Esencialmente, $evalAsync
es una versión más limpia de $timeout
con una gran diferencia: ¡el primero tiene contexto y existe en el alcance!
Esto significa que, en realidad, puede manejar ng-click
y ng-dblclick
en el mismo elemento. Tenga en cuenta, sin embargo, que esto aún activará la función de un solo clic antes de la función de doble clic . Esto debería ser suficiente:
<div ng-controller="MyController">
<a href="#"
ng-click="$evalAsync(singleClickAction())"
ng-dblclick="doubleClickAction()">
CLICK
</a>
</div>
Aquí hay un jsfiddle con la funcionalidad prevista usando Angular 1.6.4 .
Estaba buscando un manejo de eventos simple y doble clic con AngularJS, ya que AngularJS siempre dispara solo el evento ng-click incluso si hay una directiva ng-dblclick establecida para nuestro elemento.
Aquí hay algunos códigos de trabajo para aquellos que buscan una solución:
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>
Así que mi pregunta es (ya que soy un novato de AngularJS): ¿podría alguien más experimentado escribir alguna buena directiva para manejar esos dos eventos?
En mi opinión, la manera perfecta sería cambiar el comportamiento de ng-click, ng-dblclick y agregar alguna directiva "ng-sglclick" para manejar código con un solo clic. No sé si es posible, pero me parece muy útil para todos.
¡Siéntete libre de compartir tus opiniones!
Me crucé con esto y pensé que echaría una alternativa. No es muy diferente del póster original, aparte de dos puntos clave.
1) No hay declaraciones de funciones anidadas.
2) Uso $ timeout. A menudo utilizo $ timeout incluso sin demora ... especialmente si estoy lanzando promesas de hacer otro trabajo. El tiempo de espera de $ se activará cuando llegue el ciclo de resumen, lo que garantiza que se aplicará cualquier cambio de datos al alcance.
Dado
<img src="myImage.jpg" ng-click="singleClick()" ng-dblclick="doubleClick()">
En su controlador, la función singleClick se verá así:
$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);
};
Y la función de DoubleClick se verá normal:
$scope.doubleClick = function () {
$timeout(function () {
//do something with your double click here
});
};
Espero que esto ayude a alguien...
Me encontré al intentar encontrar una forma de manejar el doble clic y hacer clic al mismo tiempo. Usé los conceptos aquí para cancelar el clic original. Si se produce un segundo clic antes de la demora, se realiza la acción de doble clic. Si no hay un segundo clic, una vez que finaliza la demora, se ejecuta la acción predeterminada ngClick y el evento original se desencadena en el elemento (y se permite que burbujee como lo haría inicialmente).
Ejemplo
<div ng-click="singleClick()"><span double-click="doubleClick()">double click me</span></div>
Código
.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)
})
}
}
})
Podrías simplemente escribir el tuyo. Eché un vistazo a la forma angular que maneja el clic y lo modifiqué con el código que encontré aquí: Jquery hace doble clic y hace un solo clic por separado
<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
}
});
}
};
}])
Aquí hay un ejemplo
Uniendo las piezas de las respuestas aquí:
- usando la estrategia de @GregGrater para simplificar
- creando una
directive
, como @Rob (la que se acepta como la mejor respuesta en este hilo) - solucionando el problema de la respuesta de @Rob, al reemplazar la directiva de compilación ngClick usando @EricChen answer
Aquí el Plunker con la esencia de la idea (igual que el fragmento en esta respuesta, ver abajo).
Nota ng-dblclick
: idealmente, si no se define ng-dblclick
para el elemento, no debería evitar el solo clic (aquí un fork Plunker que implementa esta idea)
(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://.com/a/20445344/4352306
myApp.config(function($provide) { // Source: https://.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://.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>
también funciona si al llamar a singleClick
en doubleClick
no se comete ningún error
<div
onclick="var scope = angular.element(this).scope(); scope.singleClick();"
ng-click="null"
ng-dblclick="doubleClick()"
></div>