it-swarm.com.de

Handhabung von ng-click und ng-dblclick auf dasselbe Element mit AngularJS

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!

36
stealz

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

Plunker

27
Rob

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 $applys wird eine weitere Digest-Schleife starten, wodurch der $evals 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 .

24
stites

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 ...

13
Greg Grater

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

    }
  }

})
2
Jesse Houchins

Die Antworten hier zusammenfassen:

  • @GregGrater-Strategie verwenden der Einfachheit halber
  • erstellen einer directive, als @Rob (der als beste Antwort in diesem Thread akzeptiert wird)
  • lösen des Problems der @Rob-Antwort, indem Sie die Build-In-Direktive 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>

1
rellampec

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>
0
Sajjad Shirazy