Je recherchais la gestion d'événements en un ou deux clics avec AngularJS, car AngularJS ne déclenche toujours que l'événement ng-click, même si une directive ng-dblclick est définie pour notre élément.
Voici un code de travail pour ceux qui cherchent une solution:
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>
Ma question est donc la suivante (depuis que je suis un débutant dans AngularJS): quelqu'un de plus expérimenté pourrait-il écrire une directive de Nice pour gérer ces deux événements?
A mon avis, le moyen idéal serait de changer le comportement de ng-click, ng-dblclick et d'ajouter une directive "ng-sglclick" pour gérer le code en un seul clic. Je ne sais pas si c'est même possible, mais je le trouverais très utile pour tout le monde.
N'hésitez pas à partager vos opinions!
Vous pouvez simplement écrire le vôtre. J'ai jeté un coup d'œil à la manière dont le clic géré a été modifié et modifié avec le code que j'ai trouvé ici: Jquery bind double clic et clic simple séparément
<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
}
});
}
};
}])
Voici un exemple
La réponse de Greg est certainement la plus proche de la réponse la plus nette. Je vais m'appuyer sur sa réponse pour proposer une version dans laquelle aucun nouveau code ne doit être écrit ni aucune nouvelle injection ne doit être utilisée dans votre contrôleur.
La première chose à poser est de savoir pourquoi les délais d'attente sont utilisés pour résoudre ce genre de problèmes. Ils sont essentiellement utilisés pour obliger une fonction à ignorer le reste de la boucle d'événements en cours afin que l'exécution soit propre. En mode angulaire, cependant, vous vous intéressez réellement au fonctionnement de la boucle de digestion. C'est presque la même chose que votre gestionnaire d'événements classique, à l'exception de quelques différences mineures qui le rendent idéal pour l'UX. Certains outils que vous avez sous la main pour modifier l'ordre d'exécution des fonctions sont les suivants: scope.$eval
, scope.$evalAsync
, scope.$apply
et scope.$applyAsync
.
Je crois que le $apply
s lancera une autre boucle de résumé, ce qui laisse le $eval
s. $eval
exécutera tout code que vous incluez immédiatement avec le contexte du $scope
en cours et $evalAsync
mettra en file d'attente votre fonction à exécuter à la fin de la boucle de résumé. $evalAsync
est essentiellement une version plus propre de $timeout
avec une différence majeure: la première a un contexte et existe dans la portée!
Cela signifie que vous pouvez réellement manipuler ng-click
et ng-dblclick
sur le même élément. Notez cependant que cela déclenchera toujours la fonction clic simple avant la fonction double-clic. Cela devrait suffire:
<div ng-controller="MyController">
<a href="#"
ng-click="$evalAsync(singleClickAction())"
ng-dblclick="doubleClickAction()">
CLICK
</a>
</div>
Voici un jsfiddle avec la fonctionnalité prévue utilisant Angular 1.6.4 .
Je suis tombé sur cela et j'ai pensé que je proposerais une alternative. Ce n’est pas très différent de l’affiche originale, à part deux points essentiels.
1) Il n'y a pas de déclaration de fonction imbriquée.
2) J'utilise $ timeout. J'utilise souvent $ timeout même sans délai ... surtout si je lance des promesses de faire un autre travail. Le délai d'attente $ se déclenche lorsque le cycle de résumé est terminé, ce qui garantit que toutes les modifications de données apportées à la portée sont appliquées.
Donné
<img src="myImage.jpg" ng-click="singleClick()" ng-dblclick="doubleClick()">
Dans votre contrôleur, la fonction singleClick ressemblera à:
$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);
};
Et la fonction doubleClick aura l’air normal:
$scope.doubleClick = function () {
$timeout(function () {
//do something with your double click here
});
};
J'espère que ça aide quelqu'un ...
J'ai couru en essayant de trouver un moyen de gérer le double clic et le clic en même temps. J'ai utilisé les concepts ici pour annuler le clic d'origine. Si un deuxième clic se produit avant le délai, l'action du double clic est effectuée. S'il n'y a pas un deuxième clic, une fois le délai écoulé, l'action ngClick par défaut s'exécute et l'événement d'origine est déclenché sur l'élément (et autorisé à bouillonner comme il l'aurait initialement).
Exemple
<div ng-click="singleClick()"><span double-click="doubleClick()">double click me</span></div>
Code
.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)
})
}
}
})
Joindre les morceaux des réponses ici:
directive
, comme @Rob (celui accepté comme la meilleure réponse dans ce fil)ngClick
en utilisant @EricChen answerVoici le Plunker avec l’essence de l’idée (idem snippet dans cette réponse; voir ci-dessous).
Note de fin: idéalement, s'il n'y a pas de ng-dblclick
défini pour l'élément, cela ne devrait pas empêcher le simple clic (ici un Plunker fork implémentant cette idée)
(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>
cela fonctionne aussi si appeler singleClick
sur la doubleClick
ne fait aucune erreur
<div
onclick="var scope = angular.element(this).scope(); scope.singleClick();"
ng-click="null"
ng-dblclick="doubleClick()"
></div>