Ich habe sowohl nach Einzel- als auch nach Doppelklick-Ereignisbehandlung mit AngularJS gesucht, da AngularJS immer nur das ng-click-Ereignis auslöst, selbst wenn für unser Element eine ng-dblclick-Direktive gesetzt ist.
Hier ist ein funktionierender Code für diejenigen, die eine Lösung suchen:
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>
Meine Frage ist also (da ich ein Neuling von AngularJS bin): Könnte jemand, der mehr Erfahrung hat, eine Nice-Direktive schreiben, um diese beiden Ereignisse zu behandeln?
Meines Erachtens wäre der beste Weg, das Verhalten von ng-click und ng-dblclick zu ändern und eine "ng-sglclick" -Direktive für die Verarbeitung von Single-Click-Only-Code hinzuzufügen. Ich weiß nicht, ob es überhaupt möglich ist, aber ich würde es für alle sehr nützlich finden.
Fühlen Sie sich frei, Ihre Meinungen zu teilen!
Sie könnten einfach Ihre eigenen schreiben. Ich habe einen Blick darauf geworfen, wie Winkel mit Clicks umgegangen ist, und es mit Code geändert, den ich hier gefunden habe: Jquery bind Doppelklick und Einzelklick getrennt
<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
}
});
}
};
}])
Hier ist ein Beispiel
Gregs Antwort ist der saubersten Antwort auf jeden Fall am nächsten. Ich werde auf seiner Antwort aufbauen, um eine Version zu entwickeln, in der kein neuer Code geschrieben werden muss und keine neuen Injektionen in Ihrem Controller verwendet werden müssen.
Die erste Frage ist, warum Timeouts verwendet werden, um solche Probleme zu umgehen. Sie dienen im Wesentlichen dazu, dass eine Funktion den Rest der aktuellen Ereignisschleife überspringt, so dass die Ausführung sauber ist. In Angle interessieren Sie sich jedoch tatsächlich für die Funktionsweise der Digest-Schleife. Es ist fast das gleiche wie Ihr klassischer Event-Handler, mit Ausnahme einiger geringfügiger Unterschiede, die es für UX hervorragend machen. Zu den Tools, die Sie zur Verfügung haben, um die Reihenfolge der Funktionsausführung zu ändern, gehören scope.$eval
, scope.$evalAsync
, scope.$apply
und scope.$applyAsync
.
Ich glaube, der $apply
s wird eine weitere Digest-Schleife starten, wodurch der $eval
s verlassen wird. $eval
führt den von Ihnen eingeschlossenen Code sofort mit dem Kontext des aktuellen $scope
aus, und $evalAsync
wird Ihre Funktion in eine Warteschlange stellen, die am Ende der Digest-Schleife ausgeführt wird. Im Wesentlichen ist $evalAsync
eine sauberere Version von $timeout
mit einem großen Unterschied - die erste hat einen Kontext und existiert im Geltungsbereich!
Dies bedeutet, dass Sie tatsächlich ng-click
und ng-dblclick
für dasselbe Element behandeln können. Beachten Sie jedoch, dass dies die Einzelklickfunktion vor der Doppelklickfunktion auslöst. Das sollte ausreichen:
<div ng-controller="MyController">
<a href="#"
ng-click="$evalAsync(singleClickAction())"
ng-dblclick="doubleClickAction()">
CLICK
</a>
</div>
Hier ist ein Jsfiddle mit der beabsichtigten Funktionalität von Angular 1.6.4 .
Kam über das und dachte, ich würde eine Alternative werfen. Abgesehen von zwei wichtigen Punkten unterscheidet es sich nicht wesentlich vom ursprünglichen Poster.
1) Es gibt keine verschachtelten Funktionsdeklarationen.
2) Ich benutze $ timeout. Ich verwende oft $ timeout auch ohne Verzögerung ... vor allem, wenn ich verspreche, andere Aufgaben zu erledigen. Das $ timeout wird ausgelöst, wenn der Digest-Zyklus durchgeführt wird. Dadurch wird sichergestellt, dass alle Datenänderungen des Bereichs übernommen werden.
Gegeben
<img src="myImage.jpg" ng-click="singleClick()" ng-dblclick="doubleClick()">
In Ihrem Controller sieht die SingleClick-Funktion folgendermaßen aus:
$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);
};
Und die doubleClick-Funktion wird normal aussehen:
$scope.doubleClick = function () {
$timeout(function () {
//do something with your double click here
});
};
Hoffe das hilft jemandem ...
Ich bin rübergegangen, als ich versuchte, einen Weg zu finden, mit Doppelklick und Klick gleichzeitig umzugehen. Ich habe die Konzepte hier verwendet, um den ursprünglichen Klick abzubrechen. Wenn vor der Verzögerung ein zweiter Klick erfolgt, wird die Doppelklickaktion ausgeführt. Wenn kein zweiter Klick erfolgt, wird nach Ablauf der Verzögerung die standardmäßige ngClick-Aktion ausgeführt, und das ursprüngliche Ereignis wird für das Element ausgelöst (und darf wie vorher blubbern).
Beispiel
<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)
})
}
}
})
Die Antworten hier zusammenfassen:
directive
, als @Rob (der als beste Antwort in diesem Thread akzeptiert wird)ngClick
ersetzen, indem Sie @EricChen answer verwenden.Hier der Plunker mit der Essenz der Idee (wie in dieser Antwort auch ein Ausschnitt; siehe unten).
Nebenanmerkung: Wenn für das Element kein ng-dblclick
definiert ist, sollte es den Einzelklick nicht verhindern (hier ein Plunker fork, der diese Idee implementiert)
(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>
es funktioniert auch, wenn der Aufruf von singleClick
für die doubleClick
keinen Fehler macht
<div
onclick="var scope = angular.element(this).scope(); scope.singleClick();"
ng-click="null"
ng-dblclick="doubleClick()"
></div>